feat: able to push image to docker registry

This commit is contained in:
Andras Bacsai 2022-12-01 14:39:02 +01:00
parent 127880cf8d
commit 8ccb0c88db
9 changed files with 102 additions and 20 deletions

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Application" ADD COLUMN "dockerRegistryImageName" TEXT;

View File

@ -133,6 +133,7 @@ model Application {
baseBuildImage String?
settings ApplicationSettings?
dockerRegistryId String?
dockerRegistryImageName String?
simpleDockerfile String?
persistentStorage ApplicationPersistentStorage[]

View File

@ -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' } });
}
});
}

View File

@ -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) {

View File

@ -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
})
}

View File

@ -335,7 +335,8 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
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<SaveApplication>,
dockerComposeFileLocation,
dockerComposeConfiguration,
simpleDockerfile,
dockerRegistryImageName,
...defaultConfiguration,
connectedDatabase: { update: { hostedDatabaseDBName: baseDatabaseBranch } }
}
@ -398,6 +400,7 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
dockerComposeFileLocation,
dockerComposeConfiguration,
simpleDockerfile,
dockerRegistryImageName,
...defaultConfiguration
}
});

View File

@ -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 {

View File

@ -218,7 +218,7 @@
<li class="menu-title">
<span>Advanced</span>
</li>
{#if !application.simpleDockerfile}
{#if application.gitSourceId}
<li
class="rounded"
class:bg-coollabs={$page.url.pathname === `/applications/${$page.params.id}/revert`}
@ -267,7 +267,7 @@
</svg>Monitoring</a
>
</li>
{#if !application.settings.isBot && !application.simpleDockerfile}
{#if !application.settings.isBot && application.gitSourceId}
<li
class="rounded"
class:bg-coollabs={$page.url.pathname === `/applications/${$page.params.id}/previews`}

View File

@ -568,8 +568,6 @@
>
{/if}
</div>
{:else}
{/if}
<div class="grid grid-cols-2 items-center">
<label for="registry">Docker Registry</label>
@ -592,6 +590,24 @@
>
{/if}
</div>
{#if application.dockerRegistry?.id && application.gitSourceId}
<div class="grid grid-cols-2 items-center">
<label for="registry"
>Push Image to Registry <Explainer
explanation="Push the build image to the specific Docker Registry.<br><br>This is useful if you want to use the image in other places. If you don't fill this the image will be only available on the server.<br><br>Tag is optional. If you don't fill it, the tag will be the same as the git commit hash."
/></label
>
<input
name="dockerRegistryImageName"
id="dockerRegistryImageName"
readonly={isDisabled}
disabled={isDisabled}
class="w-full"
placeholder="e.g. coollabsio/myimage (tag will be commit sha) or coollabsio/myimage:tag"
bind:value={application.dockerRegistryImageName}
/>
</div>
{/if}
{#if !isSimpleDockerfile}
<div class="grid grid-cols-2 items-center">
<label for="buildPack">{$t('application.build_pack')} </label>
@ -734,7 +750,7 @@
<label for="simpleDockerfile">Dockerfile</label>
<div class="flex gap-2">
<textarea
rows=10
rows="10"
id="simpleDockerfile"
name="simpleDockerfile"
class="w-full"