From 8ccb0c88db2dc940dafacd46c774b81b91d464ac Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 1 Dec 2022 14:39:02 +0100 Subject: [PATCH] feat: able to push image to docker registry --- .../migration.sql | 2 + apps/api/prisma/schema.prisma | 1 + apps/api/src/jobs/deployApplication.ts | 61 ++++++++++++++++--- apps/api/src/lib/buildPacks/common.ts | 6 +- apps/api/src/lib/common.ts | 16 ++++- .../routes/api/v1/applications/handlers.ts | 5 +- .../src/routes/api/v1/applications/types.ts | 3 +- .../src/routes/applications/[id]/_Menu.svelte | 4 +- .../src/routes/applications/[id]/index.svelte | 24 ++++++-- 9 files changed, 102 insertions(+), 20 deletions(-) create mode 100644 apps/api/prisma/migrations/20221201133847_push_image_to_docker_registry/migration.sql diff --git a/apps/api/prisma/migrations/20221201133847_push_image_to_docker_registry/migration.sql b/apps/api/prisma/migrations/20221201133847_push_image_to_docker_registry/migration.sql new file mode 100644 index 000000000..9f85d3518 --- /dev/null +++ b/apps/api/prisma/migrations/20221201133847_push_image_to_docker_registry/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "dockerRegistryImageName" TEXT; diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma index 8c7d0d1c4..54eefc504 100644 --- a/apps/api/prisma/schema.prisma +++ b/apps/api/prisma/schema.prisma @@ -133,6 +133,7 @@ model Application { baseBuildImage String? settings ApplicationSettings? dockerRegistryId String? + dockerRegistryImageName String? simpleDockerfile String? persistentStorage ApplicationPersistentStorage[] diff --git a/apps/api/src/jobs/deployApplication.ts b/apps/api/src/jobs/deployApplication.ts index 2ae04b3aa..6203ce087 100644 --- a/apps/api/src/jobs/deployApplication.ts +++ b/apps/api/src/jobs/deployApplication.ts @@ -3,8 +3,8 @@ import crypto from 'crypto'; import fs from 'fs/promises'; import yaml from 'js-yaml'; -import { copyBaseConfigurationFiles, makeLabelForSimpleDockerfile, makeLabelForStandaloneApplication, saveBuildLog, setDefaultConfiguration } from '../lib/buildPacks/common'; -import { createDirectories, decrypt, defaultComposeConfiguration, executeDockerCmd, getDomain, prisma, decryptApplication, isDev } from '../lib/common'; +import { copyBaseConfigurationFiles, makeLabelForSimpleDockerfile, makeLabelForStandaloneApplication, saveBuildLog, saveDockerRegistryCredentials, setDefaultConfiguration } from '../lib/buildPacks/common'; +import { createDirectories, decrypt, defaultComposeConfiguration, executeDockerCmd, getDomain, prisma, decryptApplication, isDev, pushToRegistry } from '../lib/common'; import * as importers from '../lib/importers'; import * as buildpacks from '../lib/buildPacks'; @@ -37,7 +37,7 @@ import * as buildpacks from '../lib/buildPacks'; for (const queueBuild of queuedBuilds) { actions.push(async () => { - let application = await prisma.application.findUnique({ where: { id: queueBuild.applicationId }, include: { destinationDocker: true, gitSource: { include: { githubApp: true, gitlabApp: true } }, persistentStorage: true, secrets: true, settings: true, teams: true } }) + let application = await prisma.application.findUnique({ where: { id: queueBuild.applicationId }, include: { dockerRegistry: true, destinationDocker: true, gitSource: { include: { githubApp: true, gitlabApp: true } }, persistentStorage: true, secrets: true, settings: true, teams: true } }) let { id: buildId, type, gitSourceId, sourceBranch = null, pullmergeRequestId = null, previewApplicationId = null, forceRebuild, sourceRepository = null } = queueBuild application = decryptApplication(application) @@ -51,7 +51,8 @@ import * as buildpacks from '../lib/buildPacks'; port, persistentStorage, exposePort, - simpleDockerfile + simpleDockerfile, + dockerRegistry } = application const { workdir } = await createDirectories({ repository: applicationId, buildId }); try { @@ -109,6 +110,11 @@ import * as buildpacks from '../lib/buildPacks'; } await fs.writeFile(`${workdir}/Dockerfile`, simpleDockerfile); + if (dockerRegistry) { + const { url, username, password } = dockerRegistry + await saveDockerRegistryCredentials({ url, username, password, workdir }) + } + const labels = makeLabelForSimpleDockerfile({ applicationId, type, @@ -164,7 +170,7 @@ import * as buildpacks from '../lib/buildPacks'; } throw new Error(error); } - await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } }); + } } catch (error) { const foundBuild = await prisma.build.findUnique({ where: { id: buildId } }) @@ -179,10 +185,27 @@ import * as buildpacks from '../lib/buildPacks'; if (error !== 1) { await saveBuildLog({ line: error, buildId, applicationId: application.id }); } + } + try { + if (application.dockerRegistryImageName) { + const customTag = application.dockerRegistryImageName.split(':')[1] || buildId; + const imageName = application.dockerRegistryImageName.split(':')[0]; + await saveBuildLog({ line: `Pushing ${imageName}:${customTag} to Docker Registry... It could take a while...`, buildId, applicationId: application.id }); + await pushToRegistry(application, workdir, buildId, imageName, customTag) + await saveBuildLog({ line: "Success 🎉", buildId, applicationId: application.id }); + } + } catch (error) { + if (error.stdout) { + await saveBuildLog({ line: error.stdout, buildId, applicationId }); + } + if (error.stderr) { + await saveBuildLog({ line: error.stderr, buildId, applicationId }); + } } finally { if (!isDev) { await fs.rm(workdir, { recursive: true, force: true }); } + await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } }); } return; } @@ -211,6 +234,7 @@ import * as buildpacks from '../lib/buildPacks'; baseBuildImage, deploymentType, gitCommitHash, + dockerRegistry } = application let { @@ -231,7 +255,7 @@ import * as buildpacks from '../lib/buildPacks'; let imageId = applicationId; let domain = getDomain(fqdn); - + let tag = null if (pullmergeRequestId) { const previewApplications = await prisma.previewApplication.findMany({ where: { applicationId: originalApplicationId, pullmergeRequestId } }) if (previewApplications.length > 0) { @@ -330,7 +354,7 @@ import * as buildpacks from '../lib/buildPacks'; if (!commit) { throw new Error('No commit found?'); } - let tag = commit.slice(0, 7); + tag = commit.slice(0, 7); if (pullmergeRequestId) { tag = `${commit.slice(0, 7)}-${pullmergeRequestId}`; } @@ -500,6 +524,9 @@ import * as buildpacks from '../lib/buildPacks'; } await fs.writeFile(`${workdir}/.env`, envs.join('\n')); + const { url, username, password } = dockerRegistry + await saveDockerRegistryCredentials({ url, username, password, workdir }) + let envFound = false; try { envFound = !!(await fs.stat(`${workdir}/.env`)); @@ -553,7 +580,7 @@ import * as buildpacks from '../lib/buildPacks'; } throw new Error(error); } - await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } }); + if (!pullmergeRequestId) await prisma.application.update({ where: { id: applicationId }, data: { configHash: currentHash } @@ -573,10 +600,28 @@ import * as buildpacks from '../lib/buildPacks'; if (error !== 1) { await saveBuildLog({ line: error, buildId, applicationId: application.id }); } + } + try { + if (application.dockerRegistryImageName) { + const customTag = application.dockerRegistryImageName.split(':')[1] || tag; + const imageName = application.dockerRegistryImageName.split(':')[0]; + await saveBuildLog({ line: `Pushing ${imageName}:${customTag} to Docker Registry... It could take a while...`, buildId, applicationId: application.id }); + await pushToRegistry(application, workdir, tag, imageName, customTag) + await saveBuildLog({ line: "Success 🎉", buildId, applicationId: application.id }); + } + } catch (error) { + if (error.stdout) { + await saveBuildLog({ line: error.stdout, buildId, applicationId }); + } + if (error.stderr) { + await saveBuildLog({ line: error.stderr, buildId, applicationId }); + } + } finally { if (!isDev) { await fs.rm(workdir, { recursive: true, force: true }); } + await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } }); } }); } diff --git a/apps/api/src/lib/buildPacks/common.ts b/apps/api/src/lib/buildPacks/common.ts index 0f8f8f36e..b62c3f3a6 100644 --- a/apps/api/src/lib/buildPacks/common.ts +++ b/apps/api/src/lib/buildPacks/common.ts @@ -607,13 +607,13 @@ export function checkPnpm(installCommand = null, buildCommand = null, startComma } export async function saveDockerRegistryCredentials({ url, username, password, workdir }) { - let decryptedPassword = decrypt(password); - const location = `${workdir}/.docker`; - if (!username || !password) { return null } + let decryptedPassword = decrypt(password); + const location = `${workdir}/.docker`; + try { await fs.mkdir(`${workdir}/.docker`); } catch (error) { diff --git a/apps/api/src/lib/common.ts b/apps/api/src/lib/common.ts index 520802cc3..723368941 100644 --- a/apps/api/src/lib/common.ts +++ b/apps/api/src/lib/common.ts @@ -15,7 +15,7 @@ import sshConfig from 'ssh-config'; import jsonwebtoken from 'jsonwebtoken'; import { checkContainer, removeContainer } from './docker'; import { day } from './dayjs'; -import { saveBuildLog } from './buildPacks/common'; +import { saveBuildLog, saveDockerRegistryCredentials } from './buildPacks/common'; import { scheduler } from './scheduler'; export const version = '3.12.0'; @@ -1713,3 +1713,17 @@ export function decryptApplication(application: any) { return application; } } + +export async function pushToRegistry(application: any, workdir: string, tag: string, imageName: string, customTag: string) { + const location = `${workdir}/.docker` + const tagCommand = `docker tag ${application.id}:${tag} ${imageName}:${customTag}` + const pushCommand = `docker --config ${location} push ${imageName}:${customTag}` + await executeDockerCmd({ + dockerId: application.destinationDockerId, + command: tagCommand + }) + await executeDockerCmd({ + dockerId: application.destinationDockerId, + command: pushCommand + }) +} \ No newline at end of file diff --git a/apps/api/src/routes/api/v1/applications/handlers.ts b/apps/api/src/routes/api/v1/applications/handlers.ts index aa3b74ab5..bcebb21d8 100644 --- a/apps/api/src/routes/api/v1/applications/handlers.ts +++ b/apps/api/src/routes/api/v1/applications/handlers.ts @@ -335,7 +335,8 @@ export async function saveApplication(request: FastifyRequest, dockerComposeFile, dockerComposeFileLocation, dockerComposeConfiguration, - simpleDockerfile + simpleDockerfile, + dockerRegistryImageName } = request.body if (port) port = Number(port); if (exposePort) { @@ -375,6 +376,7 @@ export async function saveApplication(request: FastifyRequest, dockerComposeFileLocation, dockerComposeConfiguration, simpleDockerfile, + dockerRegistryImageName, ...defaultConfiguration, connectedDatabase: { update: { hostedDatabaseDBName: baseDatabaseBranch } } } @@ -398,6 +400,7 @@ export async function saveApplication(request: FastifyRequest, dockerComposeFileLocation, dockerComposeConfiguration, simpleDockerfile, + dockerRegistryImageName, ...defaultConfiguration } }); diff --git a/apps/api/src/routes/api/v1/applications/types.ts b/apps/api/src/routes/api/v1/applications/types.ts index adac42ed6..ee8d19580 100644 --- a/apps/api/src/routes/api/v1/applications/types.ts +++ b/apps/api/src/routes/api/v1/applications/types.ts @@ -26,7 +26,8 @@ export interface SaveApplication extends OnlyId { dockerComposeFile: string, dockerComposeFileLocation: string, dockerComposeConfiguration: string, - simpleDockerfile: string + simpleDockerfile: string, + dockerRegistryImageName: string } } export interface SaveApplicationSettings extends OnlyId { diff --git a/apps/ui/src/routes/applications/[id]/_Menu.svelte b/apps/ui/src/routes/applications/[id]/_Menu.svelte index 39a60b29b..f2dc5c47a 100644 --- a/apps/ui/src/routes/applications/[id]/_Menu.svelte +++ b/apps/ui/src/routes/applications/[id]/_Menu.svelte @@ -218,7 +218,7 @@ - {#if !application.simpleDockerfile} + {#if application.gitSourceId}
  • Monitoring
  • - {#if !application.settings.isBot && !application.simpleDockerfile} + {#if !application.settings.isBot && application.gitSourceId}
  • {/if} - {:else} - {/if}
    @@ -592,6 +590,24 @@ > {/if}
    + {#if application.dockerRegistry?.id && application.gitSourceId} +
    + + +
    + {/if} {#if !isSimpleDockerfile}
    @@ -728,13 +744,13 @@
    Configuration
    - +