wip: docker compose

This commit is contained in:
Andras Bacsai
2022-10-05 15:34:52 +02:00
parent 3f1841a188
commit d8206c0e3e
16 changed files with 831 additions and 516 deletions

View File

@@ -212,17 +212,37 @@ import * as buildpacks from '../lib/buildPacks';
//
}
await copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId, baseImage);
const labels = makeLabelForStandaloneApplication({
applicationId,
fqdn,
name,
type,
pullmergeRequestId,
buildPack,
repository,
branch,
projectId,
port: exposePort ? `${exposePort}:${port}` : port,
commit,
installCommand,
buildCommand,
startCommand,
baseDirectory,
publishDirectory
});
if (forceRebuild) deployNeeded = true
if (!imageFound || deployNeeded) {
// if (true) {
if (buildpacks[buildPack])
await buildpacks[buildPack]({
dockerId: destinationDocker.id,
network: destinationDocker.network,
buildId,
applicationId,
domain,
name,
type,
volumes,
labels,
pullmergeRequestId,
buildPack,
repository,
@@ -248,7 +268,7 @@ import * as buildpacks from '../lib/buildPacks';
denoOptions,
baseImage,
baseBuildImage,
deploymentType
deploymentType,
});
else {
await saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId });
@@ -257,112 +277,137 @@ import * as buildpacks from '../lib/buildPacks';
} else {
await saveBuildLog({ line: 'Build image already available - no rebuild required.', buildId, applicationId });
}
try {
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker stop -t 0 ${imageId}` })
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker rm ${imageId}` })
} 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}`);
}
if (buildPack === 'compose') {
try {
await executeDockerCmd({
dockerId: destinationDockerId,
command: `docker ps -a --filter 'label=coolify.applicationId=${applicationId}' --format {{.ID}}|xargs -r -n 1 docker stop -t 0`
})
await executeDockerCmd({
dockerId: destinationDockerId,
command: `docker ps -a --filter 'label=coolify.applicationId=${applicationId}' --format {{.ID}}|xargs -r -n 1 docker rm --force`
})
} catch (error) {
//
}
try {
await executeDockerCmd({ debug, buildId, applicationId, dockerId: destinationDocker.id, command: `docker compose --project-directory ${workdir} up -d` })
await saveBuildLog({ line: 'Deployment successful!', buildId, applicationId });
await saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId });
await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } });
await prisma.application.update({
where: { id: applicationId },
data: { configHash: currentHash }
});
} 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'
}
});
}
});
}
await fs.writeFile(`${workdir}/.env`, envs.join('\n'));
const labels = makeLabelForStandaloneApplication({
applicationId,
fqdn,
name,
type,
pullmergeRequestId,
buildPack,
repository,
branch,
projectId,
port: exposePort ? `${exposePort}:${port}` : port,
commit,
installCommand,
buildCommand,
startCommand,
baseDirectory,
publishDirectory
});
let envFound = false;
try {
envFound = !!(await fs.stat(`${workdir}/.env`));
} catch (error) {
//
}
try {
await saveBuildLog({ line: 'Deployment started.', buildId, applicationId });
const composeVolumes = volumes.map((volume) => {
return {
[`${volume.split(':')[0]}`]: {
name: volume.split(':')[0]
}
};
});
const composeFile = {
version: '3.8',
services: {
[imageId]: {
image: `${applicationId}:${tag}`,
container_name: imageId,
volumes,
env_file: envFound ? [`${workdir}/.env`] : [],
labels,
depends_on: [],
expose: [port],
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
// logging: {
// driver: 'fluentd',
// },
...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: 'Deployment successful!', 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);
}
} else {
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}`);
}
}
});
}
throw new Error(error);
await fs.writeFile(`${workdir}/.env`, envs.join('\n'));
let envFound = false;
try {
envFound = !!(await fs.stat(`${workdir}/.env`));
} catch (error) {
//
}
try {
await saveBuildLog({ line: 'Deployment started.', buildId, applicationId });
const composeVolumes = volumes.map((volume) => {
return {
[`${volume.split(':')[0]}`]: {
name: volume.split(':')[0]
}
};
});
const composeFile = {
version: '3.8',
services: {
[imageId]: {
image: `${applicationId}:${tag}`,
container_name: imageId,
volumes,
env_file: envFound ? [`${workdir}/.env`] : [],
labels,
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: 'Deployment successful!', 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 saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId });
await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } });
if (!pullmergeRequestId) await prisma.application.update({
where: { id: applicationId },
data: { configHash: currentHash }
});
}
await saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId });
await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } });
if (!pullmergeRequestId) await prisma.application.update({
where: { id: applicationId },
data: { configHash: currentHash }
});
}
}
catch (error) {

View File

@@ -634,6 +634,7 @@ export function makeLabelForStandaloneApplication({
return [
'coolify.managed=true',
`coolify.version=${version}`,
`coolify.applicationId=${applicationId}`,
`coolify.type=standalone-application`,
`coolify.configuration=${base64Encode(
JSON.stringify({

View File

@@ -1,34 +1,99 @@
import { promises as fs } from 'fs';
import { executeDockerCmd } from '../common';
import { buildImage } from './common';
import { defaultComposeConfiguration, executeDockerCmd } from '../common';
import { buildImage, saveBuildLog } from './common';
import yaml from 'js-yaml';
import { getSecrets } from '../../routes/api/v1/applications/handlers';
export default async function (data) {
let {
applicationId,
let {
applicationId,
debug,
buildId,
dockerId,
debug,
tag,
workdir,
buildId,
baseDirectory,
secrets,
pullmergeRequestId,
dockerFileLocation
} = data
const file = `${workdir}${baseDirectory}/docker-compose.yml`;
const dockerComposeRaw = await fs.readFile(`${file}`, 'utf8')
network,
volumes,
labels,
workdir,
baseDirectory,
secrets,
pullmergeRequestId,
port
} = data
const fileYml = `${workdir}${baseDirectory}/docker-compose.yml`;
const fileYaml = `${workdir}${baseDirectory}/docker-compose.yaml`;
let dockerComposeRaw = null;
let isYml = false;
try {
dockerComposeRaw = await fs.readFile(`${fileYml}`, 'utf8')
isYml = true
} catch (error) { }
try {
dockerComposeRaw = await fs.readFile(`${fileYaml}`, 'utf8')
} catch (error) { }
if (!dockerComposeRaw) {
throw ('docker-compose.yml or docker-compose.yaml are not found!');
}
const dockerComposeYaml = yaml.load(dockerComposeRaw)
if (!dockerComposeYaml.services) {
throw 'No Services found in docker-compose file.'
}
const envs = [
`PORT=${port}`
];
if (getSecrets.length > 0) {
secrets.forEach((secret) => {
if (secret.isBuildSecret) {
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) {
//
}
const composeVolumes = volumes.map((volume) => {
return {
[`${volume.split(':')[0]}`]: {
name: volume.split(':')[0]
}
};
});
let networks = {}
for (let [key, value] of Object.entries(dockerComposeYaml.services)) {
value['container_name'] = `${applicationId}-${key}`
console.log({key, value});
value['env_file'] = envFound ? [`${workdir}/.env`] : []
value['labels'] = labels
value['volumes'] = volumes
if (value['networks']?.length > 0) {
value['networks'].forEach((network) => {
networks[network] = {
name: network
}
})
}
value['networks'] = [...value['networks'] || '', network]
dockerComposeYaml.services[key] = { ...dockerComposeYaml.services[key], restart: defaultComposeConfiguration(network).restart, deploy: defaultComposeConfiguration(network).deploy }
}
throw 'Halting'
// await executeDockerCmd({ debug, buildId, applicationId, dockerId, command: `docker compose --project-directory ${workdir} pull` })
// await executeDockerCmd({ debug, buildId, applicationId, dockerId, command: `docker compose --project-directory ${workdir} build --progress plain --pull` })
// await executeDockerCmd({ debug, buildId, applicationId, dockerId, command: `docker compose --project-directory ${workdir} up -d` })
dockerComposeYaml['volumes'] = Object.assign({ ...dockerComposeYaml['volumes'] }, ...composeVolumes)
dockerComposeYaml['networks'] = Object.assign({ ...networks }, { [network]: { external: true } })
await fs.writeFile(`${workdir}/docker-compose.${isYml ? 'yml' : 'yaml'}`, yaml.dump(dockerComposeYaml));
await executeDockerCmd({ debug, buildId, applicationId, dockerId, command: `docker compose --project-directory ${workdir} pull` })
await saveBuildLog({ line: 'Pulling images from Compose file.', buildId, applicationId });
await executeDockerCmd({ debug, buildId, applicationId, dockerId, command: `docker compose --project-directory ${workdir} build --progress plain` })
await saveBuildLog({ line: 'Building images from Compose file.', buildId, applicationId });
}

View File

@@ -289,13 +289,16 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
baseImage,
baseBuildImage,
deploymentType,
baseDatabaseBranch
baseDatabaseBranch,
dockerComposeFile,
dockerComposeFileLocation,
dockerComposeConfiguration
} = request.body
console.log({dockerComposeConfiguration})
if (port) port = Number(port);
if (exposePort) {
exposePort = Number(exposePort);
}
const { destinationDocker: { engine, remoteEngine, remoteIpAddress }, exposePort: configuredPort } = await prisma.application.findUnique({ where: { id }, include: { destinationDocker: true } })
if (exposePort) await checkExposedPort({ id, configuredPort, exposePort, engine, remoteEngine, remoteIpAddress })
if (denoOptions) denoOptions = denoOptions.trim();
@@ -324,6 +327,9 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
baseImage,
baseBuildImage,
deploymentType,
dockerComposeFile,
dockerComposeFileLocation,
dockerComposeConfiguration,
...defaultConfiguration,
connectedDatabase: { update: { hostedDatabaseDBName: baseDatabaseBranch } }
}
@@ -342,6 +348,9 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
baseImage,
baseBuildImage,
deploymentType,
dockerComposeFile,
dockerComposeFileLocation,
dockerComposeConfiguration,
...defaultConfiguration
}
});

View File

@@ -21,7 +21,10 @@ export interface SaveApplication extends OnlyId {
baseImage: string,
baseBuildImage: string,
deploymentType: string,
baseDatabaseBranch: string
baseDatabaseBranch: string,
dockerComposeFile: string,
dockerComposeFileLocation: string,
dockerComposeConfiguration: string
}
}
export interface SaveApplicationSettings extends OnlyId {