From 766a25ccad874d92e01f2e07c19e6861b76b8955 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 2 Nov 2024 14:17:21 -0600 Subject: [PATCH 1/2] fix(registry): add image tag resolution correctly when using cluster --- .../server/wss/docker-container-logs.ts | 2 - apps/dokploy/server/wss/terminal.ts | 1 - packages/server/src/db/schema/registry.ts | 11 ++-- packages/server/src/setup/server-setup.ts | 1 - packages/server/src/utils/builders/index.ts | 36 ++++++---- packages/server/src/utils/cluster/upload.ts | 65 ++++++++++++++----- 6 files changed, 79 insertions(+), 37 deletions(-) diff --git a/apps/dokploy/server/wss/docker-container-logs.ts b/apps/dokploy/server/wss/docker-container-logs.ts index 63a0b89e..1493f698 100644 --- a/apps/dokploy/server/wss/docker-container-logs.ts +++ b/apps/dokploy/server/wss/docker-container-logs.ts @@ -63,7 +63,6 @@ export const setupDockerContainerLogsWebSocketServer = ( } stream .on("close", () => { - console.log("Connection closed ✅ Container Logs"); client.end(); ws.close(); }) @@ -88,7 +87,6 @@ export const setupDockerContainerLogsWebSocketServer = ( privateKey: server.sshKey?.privateKey, }); ws.on("close", () => { - console.log("Connection closed ✅, From Container Logs WS"); client.end(); }); } else { diff --git a/apps/dokploy/server/wss/terminal.ts b/apps/dokploy/server/wss/terminal.ts index 548d5e45..eb0bf2e2 100644 --- a/apps/dokploy/server/wss/terminal.ts +++ b/apps/dokploy/server/wss/terminal.ts @@ -113,7 +113,6 @@ export const setupTerminalWebSocketServer = ( }); ws.on("close", () => { - console.log("Connection closed ✅"); stream.end(); }); }, diff --git a/packages/server/src/db/schema/registry.ts b/packages/server/src/db/schema/registry.ts index ee1bab94..eaccb58b 100644 --- a/packages/server/src/db/schema/registry.ts +++ b/packages/server/src/db/schema/registry.ts @@ -1,11 +1,10 @@ -import { relations, sql } from "drizzle-orm"; -import { boolean, pgEnum, pgTable, text, timestamp } from "drizzle-orm/pg-core"; +import { relations } from "drizzle-orm"; +import { pgEnum, pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { admins } from "./admin"; import { applications } from "./application"; -import { auth } from "./auth"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. @@ -48,7 +47,7 @@ const createSchema = createInsertSchema(registry, { registryUrl: z.string(), adminId: z.string().min(1), registryId: z.string().min(1), - registryType: z.enum(["selfHosted", "cloud"]), + registryType: z.enum(["cloud"]), imagePrefix: z.string().nullable().optional(), }); @@ -59,7 +58,7 @@ export const apiCreateRegistry = createSchema username: z.string().min(1), password: z.string().min(1), registryUrl: z.string(), - registryType: z.enum(["selfHosted", "cloud"]), + registryType: z.enum(["cloud"]), imagePrefix: z.string().nullable().optional(), }) .required() @@ -72,7 +71,7 @@ export const apiTestRegistry = createSchema.pick({}).extend({ username: z.string().min(1), password: z.string().min(1), registryUrl: z.string(), - registryType: z.enum(["selfHosted", "cloud"]), + registryType: z.enum(["cloud"]), imagePrefix: z.string().nullable().optional(), serverId: z.string().optional(), }); diff --git a/packages/server/src/setup/server-setup.ts b/packages/server/src/setup/server-setup.ts index efd70a0d..f2abd3c5 100644 --- a/packages/server/src/setup/server-setup.ts +++ b/packages/server/src/setup/server-setup.ts @@ -101,7 +101,6 @@ const installRequirements = async (serverId: string, logPath: string) => { } stream .on("close", () => { - writeStream.write("Connection closed ✅"); client.end(); resolve(); }) diff --git a/packages/server/src/utils/builders/index.ts b/packages/server/src/utils/builders/index.ts index cf155e45..35ac28cb 100644 --- a/packages/server/src/utils/builders/index.ts +++ b/packages/server/src/utils/builders/index.ts @@ -1,7 +1,8 @@ import { createWriteStream } from "node:fs"; +import { join } from "node:path"; import type { InferResultType } from "@dokploy/server/types/with"; import type { CreateServiceOptions } from "dockerode"; -import { uploadImage } from "../cluster/upload"; +import { uploadImage, uploadImageRemoteCommand } from "../cluster/upload"; import { calculateResources, generateBindMounts, @@ -69,19 +70,30 @@ export const getBuildCommand = ( application: ApplicationNested, logPath: string, ) => { - const { buildType } = application; + let command = ""; + const { buildType, registry } = application; switch (buildType) { case "nixpacks": - return getNixpacksCommand(application, logPath); + command = getNixpacksCommand(application, logPath); + break; case "heroku_buildpacks": - return getHerokuCommand(application, logPath); + command = getHerokuCommand(application, logPath); + break; case "paketo_buildpacks": - return getPaketoCommand(application, logPath); + command = getPaketoCommand(application, logPath); + break; case "static": - return getStaticCommand(application, logPath); + command = getStaticCommand(application, logPath); + break; case "dockerfile": - return getDockerCommand(application, logPath); + command = getDockerCommand(application, logPath); + break; } + if (registry) { + command += uploadImageRemoteCommand(application, logPath); + } + + return command; }; export const mechanizeDockerContainer = async ( @@ -186,11 +198,11 @@ const getImageName = (application: ApplicationNested) => { return dockerImage || "ERROR-NO-IMAGE-PROVIDED"; } - const registryUrl = registry?.registryUrl || ""; - const imagePrefix = registry?.imagePrefix ? `${registry.imagePrefix}/` : ""; - return registry - ? `${registryUrl}/${imagePrefix}${appName}` - : `${appName}:latest`; + if (registry) { + return join(registry.registryUrl, registry.imagePrefix || "", appName); + } + + return `${appName}:latest`; }; const getAuthConfig = (application: ApplicationNested) => { diff --git a/packages/server/src/utils/cluster/upload.ts b/packages/server/src/utils/cluster/upload.ts index 8cca9e3d..a5aff7f4 100644 --- a/packages/server/src/utils/cluster/upload.ts +++ b/packages/server/src/utils/cluster/upload.ts @@ -1,4 +1,5 @@ import type { WriteStream } from "node:fs"; +import { join } from "node:path"; import type { ApplicationNested } from "../builders"; import { spawnAsync } from "../process/spawnAsync"; @@ -16,23 +17,14 @@ export const uploadImage = async ( const { appName } = application; const imageName = `${appName}:latest`; - const finalURL = - registryType === "selfHosted" - ? process.env.NODE_ENV === "development" - ? "localhost:5000" - : registryUrl - : registryUrl; + const finalURL = registryUrl; - const registryTag = imagePrefix - ? `${finalURL}/${imagePrefix}/${imageName}` - : `${finalURL}/${imageName}`; + const registryTag = join(finalURL, imagePrefix || "", imageName); try { - console.log(finalURL, registryTag); writeStream.write( `📦 [Enabled Registry] Uploading image to ${registry.registryType} | ${registryTag} | ${finalURL}\n`, ); - await spawnAsync( "docker", ["login", finalURL, "-u", registry.username, "-p", registry.password], @@ -49,6 +41,8 @@ export const uploadImage = async ( } }); + console.log("docker push ", registryTag); + await spawnAsync("docker", ["push", registryTag], (data) => { if (writeStream.writable) { writeStream.write(data); @@ -59,7 +53,48 @@ export const uploadImage = async ( throw error; } }; -// docker: -// endpoint: "unix:///var/run/docker.sock" -// exposedByDefault: false -// swarmMode: true + +export const uploadImageRemoteCommand = ( + application: ApplicationNested, + logPath: string, +) => { + const registry = application.registry; + + if (!registry) { + throw new Error("Registry not found"); + } + + const { registryUrl, imagePrefix } = registry; + const { appName } = application; + const imageName = `${appName}:latest`; + + const finalURL = registryUrl; + + const registryTag = join(finalURL, imagePrefix || "", imageName); + + try { + const command = ` + echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" >> ${logPath}; + docker login ${finalURL} -u ${registry.username} -p ${registry.password} >> ${logPath} 2>> ${logPath} || { + echo "❌ DockerHub Failed" >> ${logPath}; + exit 1; + } + echo "✅ DockerHub Login Success" >> ${logPath}; + docker tag ${imageName} ${registryTag} >> ${logPath} 2>> ${logPath} || { + echo "❌ Error tagging image" >> ${logPath}; + exit 1; + } + echo "✅ Image Tagged" >> ${logPath}; + + docker push ${registryTag} 2>> ${logPath} || { + echo "❌ Error pushing image" >> ${logPath}; + exit 1; + } + echo "✅ Image Pushed" >> ${logPath}; + `; + return command; + } catch (error) { + console.log(error); + throw error; + } +}; From d901b02e9285b54de099ca2921f4b784ea5a6780 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 2 Nov 2024 14:18:00 -0600 Subject: [PATCH 2/2] refactor(dokploy): remove log --- packages/server/src/utils/cluster/upload.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/server/src/utils/cluster/upload.ts b/packages/server/src/utils/cluster/upload.ts index a5aff7f4..97b5fa2e 100644 --- a/packages/server/src/utils/cluster/upload.ts +++ b/packages/server/src/utils/cluster/upload.ts @@ -41,8 +41,6 @@ export const uploadImage = async ( } }); - console.log("docker push ", registryTag); - await spawnAsync("docker", ["push", registryTag], (data) => { if (writeStream.writable) { writeStream.write(data);