fix(registry): add image tag resolution correctly when using cluster

This commit is contained in:
Mauricio Siu
2024-11-02 14:17:21 -06:00
parent fae97b1817
commit 766a25ccad
6 changed files with 79 additions and 37 deletions

View File

@@ -63,7 +63,6 @@ export const setupDockerContainerLogsWebSocketServer = (
} }
stream stream
.on("close", () => { .on("close", () => {
console.log("Connection closed ✅ Container Logs");
client.end(); client.end();
ws.close(); ws.close();
}) })
@@ -88,7 +87,6 @@ export const setupDockerContainerLogsWebSocketServer = (
privateKey: server.sshKey?.privateKey, privateKey: server.sshKey?.privateKey,
}); });
ws.on("close", () => { ws.on("close", () => {
console.log("Connection closed ✅, From Container Logs WS");
client.end(); client.end();
}); });
} else { } else {

View File

@@ -113,7 +113,6 @@ export const setupTerminalWebSocketServer = (
}); });
ws.on("close", () => { ws.on("close", () => {
console.log("Connection closed ✅");
stream.end(); stream.end();
}); });
}, },

View File

@@ -1,11 +1,10 @@
import { relations, sql } from "drizzle-orm"; import { relations } from "drizzle-orm";
import { boolean, pgEnum, pgTable, text, timestamp } from "drizzle-orm/pg-core"; import { pgEnum, pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod"; import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { z } from "zod"; import { z } from "zod";
import { admins } from "./admin"; import { admins } from "./admin";
import { applications } from "./application"; 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 * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
* database instance for multiple projects. * database instance for multiple projects.
@@ -48,7 +47,7 @@ const createSchema = createInsertSchema(registry, {
registryUrl: z.string(), registryUrl: z.string(),
adminId: z.string().min(1), adminId: z.string().min(1),
registryId: z.string().min(1), registryId: z.string().min(1),
registryType: z.enum(["selfHosted", "cloud"]), registryType: z.enum(["cloud"]),
imagePrefix: z.string().nullable().optional(), imagePrefix: z.string().nullable().optional(),
}); });
@@ -59,7 +58,7 @@ export const apiCreateRegistry = createSchema
username: z.string().min(1), username: z.string().min(1),
password: z.string().min(1), password: z.string().min(1),
registryUrl: z.string(), registryUrl: z.string(),
registryType: z.enum(["selfHosted", "cloud"]), registryType: z.enum(["cloud"]),
imagePrefix: z.string().nullable().optional(), imagePrefix: z.string().nullable().optional(),
}) })
.required() .required()
@@ -72,7 +71,7 @@ export const apiTestRegistry = createSchema.pick({}).extend({
username: z.string().min(1), username: z.string().min(1),
password: z.string().min(1), password: z.string().min(1),
registryUrl: z.string(), registryUrl: z.string(),
registryType: z.enum(["selfHosted", "cloud"]), registryType: z.enum(["cloud"]),
imagePrefix: z.string().nullable().optional(), imagePrefix: z.string().nullable().optional(),
serverId: z.string().optional(), serverId: z.string().optional(),
}); });

View File

@@ -101,7 +101,6 @@ const installRequirements = async (serverId: string, logPath: string) => {
} }
stream stream
.on("close", () => { .on("close", () => {
writeStream.write("Connection closed ✅");
client.end(); client.end();
resolve(); resolve();
}) })

View File

@@ -1,7 +1,8 @@
import { createWriteStream } from "node:fs"; import { createWriteStream } from "node:fs";
import { join } from "node:path";
import type { InferResultType } from "@dokploy/server/types/with"; import type { InferResultType } from "@dokploy/server/types/with";
import type { CreateServiceOptions } from "dockerode"; import type { CreateServiceOptions } from "dockerode";
import { uploadImage } from "../cluster/upload"; import { uploadImage, uploadImageRemoteCommand } from "../cluster/upload";
import { import {
calculateResources, calculateResources,
generateBindMounts, generateBindMounts,
@@ -69,19 +70,30 @@ export const getBuildCommand = (
application: ApplicationNested, application: ApplicationNested,
logPath: string, logPath: string,
) => { ) => {
const { buildType } = application; let command = "";
const { buildType, registry } = application;
switch (buildType) { switch (buildType) {
case "nixpacks": case "nixpacks":
return getNixpacksCommand(application, logPath); command = getNixpacksCommand(application, logPath);
break;
case "heroku_buildpacks": case "heroku_buildpacks":
return getHerokuCommand(application, logPath); command = getHerokuCommand(application, logPath);
break;
case "paketo_buildpacks": case "paketo_buildpacks":
return getPaketoCommand(application, logPath); command = getPaketoCommand(application, logPath);
break;
case "static": case "static":
return getStaticCommand(application, logPath); command = getStaticCommand(application, logPath);
break;
case "dockerfile": case "dockerfile":
return getDockerCommand(application, logPath); command = getDockerCommand(application, logPath);
break;
} }
if (registry) {
command += uploadImageRemoteCommand(application, logPath);
}
return command;
}; };
export const mechanizeDockerContainer = async ( export const mechanizeDockerContainer = async (
@@ -186,11 +198,11 @@ const getImageName = (application: ApplicationNested) => {
return dockerImage || "ERROR-NO-IMAGE-PROVIDED"; return dockerImage || "ERROR-NO-IMAGE-PROVIDED";
} }
const registryUrl = registry?.registryUrl || ""; if (registry) {
const imagePrefix = registry?.imagePrefix ? `${registry.imagePrefix}/` : ""; return join(registry.registryUrl, registry.imagePrefix || "", appName);
return registry }
? `${registryUrl}/${imagePrefix}${appName}`
: `${appName}:latest`; return `${appName}:latest`;
}; };
const getAuthConfig = (application: ApplicationNested) => { const getAuthConfig = (application: ApplicationNested) => {

View File

@@ -1,4 +1,5 @@
import type { WriteStream } from "node:fs"; import type { WriteStream } from "node:fs";
import { join } from "node:path";
import type { ApplicationNested } from "../builders"; import type { ApplicationNested } from "../builders";
import { spawnAsync } from "../process/spawnAsync"; import { spawnAsync } from "../process/spawnAsync";
@@ -16,23 +17,14 @@ export const uploadImage = async (
const { appName } = application; const { appName } = application;
const imageName = `${appName}:latest`; const imageName = `${appName}:latest`;
const finalURL = const finalURL = registryUrl;
registryType === "selfHosted"
? process.env.NODE_ENV === "development"
? "localhost:5000"
: registryUrl
: registryUrl;
const registryTag = imagePrefix const registryTag = join(finalURL, imagePrefix || "", imageName);
? `${finalURL}/${imagePrefix}/${imageName}`
: `${finalURL}/${imageName}`;
try { try {
console.log(finalURL, registryTag);
writeStream.write( writeStream.write(
`📦 [Enabled Registry] Uploading image to ${registry.registryType} | ${registryTag} | ${finalURL}\n`, `📦 [Enabled Registry] Uploading image to ${registry.registryType} | ${registryTag} | ${finalURL}\n`,
); );
await spawnAsync( await spawnAsync(
"docker", "docker",
["login", finalURL, "-u", registry.username, "-p", registry.password], ["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) => { await spawnAsync("docker", ["push", registryTag], (data) => {
if (writeStream.writable) { if (writeStream.writable) {
writeStream.write(data); writeStream.write(data);
@@ -59,7 +53,48 @@ export const uploadImage = async (
throw error; throw error;
} }
}; };
// docker:
// endpoint: "unix:///var/run/docker.sock" export const uploadImageRemoteCommand = (
// exposedByDefault: false application: ApplicationNested,
// swarmMode: true 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;
}
};