feat: simpleDockerfile deployment

This commit is contained in:
Andras Bacsai
2022-12-01 12:58:45 +01:00
parent a129be0dbd
commit 2e56086661
21 changed files with 459 additions and 184 deletions

View File

@@ -3,7 +3,7 @@ import crypto from 'crypto';
import fs from 'fs/promises';
import yaml from 'js-yaml';
import { copyBaseConfigurationFiles, makeLabelForStandaloneApplication, saveBuildLog, setDefaultConfiguration } from '../lib/buildPacks/common';
import { copyBaseConfigurationFiles, makeLabelForSimpleDockerfile, makeLabelForStandaloneApplication, saveBuildLog, setDefaultConfiguration } from '../lib/buildPacks/common';
import { createDirectories, decrypt, defaultComposeConfiguration, executeDockerCmd, getDomain, prisma, decryptApplication, isDev } from '../lib/common';
import * as importers from '../lib/importers';
import * as buildpacks from '../lib/buildPacks';
@@ -39,10 +39,155 @@ import * as buildpacks from '../lib/buildPacks';
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 { id: buildId, type, sourceBranch = null, pullmergeRequestId = null, previewApplicationId = null, forceRebuild, sourceRepository = null } = queueBuild
let { id: buildId, type, gitSourceId, sourceBranch = null, pullmergeRequestId = null, previewApplicationId = null, forceRebuild, sourceRepository = null } = queueBuild
application = decryptApplication(application)
if (!gitSourceId && application.simpleDockerfile) {
const {
id: applicationId,
destinationDocker,
destinationDockerId,
secrets,
port,
persistentStorage,
exposePort,
simpleDockerfile
} = application
const { workdir } = await createDirectories({ repository: applicationId, buildId });
try {
if (queueBuild.status === 'running') {
await saveBuildLog({ line: 'Building halted, restarting...', buildId, applicationId: application.id });
}
const volumes =
persistentStorage?.map((storage) => {
if (storage.oldPath) {
return `${applicationId}${storage.path.replace(/\//gi, '-').replace('-app', '')}:${storage.path}`;
}
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`;
}) || [];
if (destinationDockerId) {
await prisma.build.update({ where: { id: buildId }, data: { status: 'running' } });
try {
await executeDockerCmd({
dockerId: destinationDockerId,
command: `docker ps -a --filter 'label=com.docker.compose.service=${applicationId}' --format {{.ID}}|xargs -r -n 1 docker stop -t 0`
})
await executeDockerCmd({
dockerId: destinationDockerId,
command: `docker ps -a --filter 'label=com.docker.compose.service=${applicationId}' --format {{.ID}}|xargs -r -n 1 docker rm --force`
})
} catch (error) {
//
}
const envs = [
`PORT=${port}`
];
if (secrets.length > 0) {
secrets.forEach((secret) => {
if (pullmergeRequestId) {
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
if (isSecretFound.length > 0) {
envs.push(`${secret.name}=${isSecretFound[0].value}`);
} else {
envs.push(`${secret.name}=${secret.value}`);
}
} else {
if (!secret.isPRMRSecret) {
envs.push(`${secret.name}=${secret.value}`);
}
}
});
}
await fs.writeFile(`${workdir}/.env`, envs.join('\n'));
let envFound = false;
try {
envFound = !!(await fs.stat(`${workdir}/.env`));
} catch (error) {
//
}
await fs.writeFile(`${workdir}/Dockerfile`, simpleDockerfile);
const labels = makeLabelForSimpleDockerfile({
applicationId,
type,
port: exposePort ? `${exposePort}:${port}` : port,
});
try {
await saveBuildLog({ line: 'Deployment initiated', buildId, applicationId });
const composeVolumes = volumes.map((volume) => {
return {
[`${volume.split(':')[0]}`]: {
name: volume.split(':')[0]
}
};
});
const composeFile = {
version: '3.8',
services: {
[applicationId]: {
build: {
context: workdir,
},
image: `${applicationId}:${buildId}`,
container_name: applicationId,
volumes,
labels,
env_file: envFound ? [`${workdir}/.env`] : [],
depends_on: [],
expose: [port],
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
...defaultComposeConfiguration(destinationDocker.network),
}
},
networks: {
[destinationDocker.network]: {
external: true
}
},
volumes: Object.assign({}, ...composeVolumes)
};
await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile));
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker compose --project-directory ${workdir} up -d` })
await saveBuildLog({ line: 'Deployed successfully 🎉', buildId, applicationId });
} catch (error) {
await saveBuildLog({ line: error, buildId, applicationId });
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } })
if (foundBuild) {
await prisma.build.update({
where: { id: buildId },
data: {
status: 'failed'
}
});
}
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 } })
if (foundBuild) {
await prisma.build.update({
where: { id: buildId },
data: {
status: 'failed'
}
});
}
if (error !== 1) {
await saveBuildLog({ line: error, buildId, applicationId: application.id });
}
} finally {
if (!isDev) {
await fs.rm(workdir, { recursive: true, force: true });
}
}
return;
}
const originalApplicationId = application.id
const {
id: applicationId,
@@ -415,8 +560,7 @@ import * as buildpacks from '../lib/buildPacks';
});
}
}
}
catch (error) {
} catch (error) {
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } })
if (foundBuild) {
await prisma.build.update({

View File

@@ -675,7 +675,14 @@ export async function buildImage({
await saveBuildLog({ line: `Building production image built successful 🎉`, buildId, applicationId });
}
}
export function makeLabelForSimpleDockerfile({ applicationId, port, type }) {
return [
'coolify.managed=true',
`coolify.version=${version}`,
`coolify.applicationId=${applicationId}`,
`coolify.type=standalone-application`
];
}
export function makeLabelForStandaloneApplication({
applicationId,
fqdn,

View File

@@ -1091,7 +1091,7 @@ export const createDirectories = async ({
repository: string;
buildId: string;
}): Promise<{ workdir: string; repodir: string }> => {
repository = repository.replaceAll(' ', '')
if (repository) repository = repository.replaceAll(' ', '')
const repodir = `/tmp/build-sources/${repository}/`;
const workdir = `/tmp/build-sources/${repository}/${buildId}`;
let workdirFound = false;

View File

@@ -41,14 +41,13 @@ export async function checkContainer({ dockerId, container, remove = false }: {
`docker rm ${container}`
});
}
return {
found: containerFound,
status: {
isRunning,
isRestarting,
isExited
}
};
} catch (err) {

View File

@@ -334,7 +334,8 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
baseDatabaseBranch,
dockerComposeFile,
dockerComposeFileLocation,
dockerComposeConfiguration
dockerComposeConfiguration,
simpleDockerfile
} = request.body
if (port) port = Number(port);
if (exposePort) {
@@ -373,6 +374,7 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
dockerComposeFile,
dockerComposeFileLocation,
dockerComposeConfiguration,
simpleDockerfile,
...defaultConfiguration,
connectedDatabase: { update: { hostedDatabaseDBName: baseDatabaseBranch } }
}
@@ -395,6 +397,7 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
dockerComposeFile,
dockerComposeFileLocation,
dockerComposeConfiguration,
simpleDockerfile,
...defaultConfiguration
}
});
@@ -770,22 +773,37 @@ export async function deployApplication(request: FastifyRequest<DeployApplicatio
await prisma.application.update({ where: { id }, data: { configHash } });
}
await prisma.application.update({ where: { id }, data: { updatedAt: new Date() } });
await prisma.build.create({
data: {
id: buildId,
applicationId: id,
sourceBranch: branch,
branch: application.branch,
pullmergeRequestId: pullmergeRequestId?.toString(),
forceRebuild,
destinationDockerId: application.destinationDocker?.id,
gitSourceId: application.gitSource?.id,
githubAppId: application.gitSource?.githubApp?.id,
gitlabAppId: application.gitSource?.gitlabApp?.id,
status: 'queued',
type: pullmergeRequestId ? application.gitSource?.githubApp?.id ? 'manual_pr' : 'manual_mr' : 'manual'
}
});
if (application.gitSourceId) {
await prisma.build.create({
data: {
id: buildId,
applicationId: id,
sourceBranch: branch,
branch: application.branch,
pullmergeRequestId: pullmergeRequestId?.toString(),
forceRebuild,
destinationDockerId: application.destinationDocker?.id,
gitSourceId: application.gitSource?.id,
githubAppId: application.gitSource?.githubApp?.id,
gitlabAppId: application.gitSource?.gitlabApp?.id,
status: 'queued',
type: pullmergeRequestId ? application.gitSource?.githubApp?.id ? 'manual_pr' : 'manual_mr' : 'manual'
}
});
} else {
await prisma.build.create({
data: {
id: buildId,
applicationId: id,
branch: 'latest',
forceRebuild,
destinationDockerId: application.destinationDocker?.id,
status: 'queued',
type: 'manual'
}
});
}
return {
buildId
};
@@ -800,20 +818,29 @@ export async function deployApplication(request: FastifyRequest<DeployApplicatio
export async function saveApplicationSource(request: FastifyRequest<SaveApplicationSource>, reply: FastifyReply) {
try {
const { id } = request.params
const { gitSourceId, forPublic, type } = request.body
const { gitSourceId, forPublic, type, simpleDockerfile } = request.body
if (forPublic) {
const publicGit = await prisma.gitSource.findFirst({ where: { type, forPublic } });
if (gitSourceId) {
await prisma.application.update({
where: { id },
data: { gitSource: { connect: { id: gitSourceId } } }
});
} else {
const publicGit = await prisma.gitSource.findFirst({ where: { type, forPublic } });
await prisma.application.update({
where: { id },
data: { gitSource: { connect: { id: publicGit.id } } }
});
}
}
if (simpleDockerfile) {
await prisma.application.update({
where: { id },
data: { gitSource: { connect: { id: publicGit.id } } }
});
} else {
await prisma.application.update({
where: { id },
data: { gitSource: { connect: { id: gitSourceId } } }
data: { simpleDockerfile }
});
}
return reply.code(201).send()
} catch ({ status, message }) {
return errorHandler({ status, message })
@@ -916,11 +943,11 @@ export async function getBuildPack(request) {
const teamId = request.user?.teamId;
const application: any = await getApplicationFromDB(id, teamId);
return {
type: application.gitSource.type,
type: application.gitSource?.type || 'dockerRegistry',
projectId: application.projectId,
repository: application.repository,
branch: application.branch,
apiUrl: application.gitSource.apiUrl,
apiUrl: application.gitSource?.apiUrl || null,
isPublicRepository: application.settings.isPublicRepository
}
} catch ({ status, message }) {

View File

@@ -25,7 +25,8 @@ export interface SaveApplication extends OnlyId {
baseDatabaseBranch: string,
dockerComposeFile: string,
dockerComposeFileLocation: string,
dockerComposeConfiguration: string
dockerComposeConfiguration: string,
simpleDockerfile: string
}
}
export interface SaveApplicationSettings extends OnlyId {
@@ -56,7 +57,7 @@ export interface GetImages {
Body: { buildPack: string, deploymentType: string }
}
export interface SaveApplicationSource extends OnlyId {
Body: { gitSourceId?: string | null, forPublic?: boolean, type?: string }
Body: { gitSourceId?: string | null, forPublic?: boolean, type?: string, simpleDockerfile?: string }
}
export interface CheckRepository extends OnlyId {
Querystring: { repository: string, branch: string }

View File

@@ -190,7 +190,7 @@ export async function showDashboard(request: FastifyRequest) {
let foundUnconfiguredApplication = false;
for (const application of applications) {
if (!application.buildPack || !application.destinationDockerId || !application.branch || (!application.settings?.isBot && !application?.fqdn) && application.buildPack !== "compose") {
if (((!application.buildPack || !application.branch) && !application.simpleDockerfile) || !application.destinationDockerId || (!application.settings?.isBot && !application?.fqdn) && application.buildPack !== "compose") {
foundUnconfiguredApplication = true
}
}