fix: cleanupstorage

This commit is contained in:
Andras Bacsai
2022-12-26 21:17:53 +01:00
parent d25a9d7515
commit 4680b63911
4 changed files with 201 additions and 145 deletions

View File

@@ -6,14 +6,27 @@ import cookie from '@fastify/cookie';
import multipart from '@fastify/multipart'; import multipart from '@fastify/multipart';
import path, { join } from 'path'; import path, { join } from 'path';
import autoLoad from '@fastify/autoload'; import autoLoad from '@fastify/autoload';
import socketIO from 'fastify-socket.io' import socketIO from 'fastify-socket.io';
import socketIOServer from './realtime' import socketIOServer from './realtime';
import { cleanupDockerStorage, createRemoteEngineConfiguration, decrypt, executeCommand, generateDatabaseConfiguration, isDev, listSettings, prisma, sentryDSN, startTraefikProxy, startTraefikTCPProxy, version } from './lib/common'; import {
cleanupDockerStorage,
createRemoteEngineConfiguration,
decrypt,
executeCommand,
generateDatabaseConfiguration,
isDev,
listSettings,
prisma,
sentryDSN,
startTraefikProxy,
startTraefikTCPProxy,
version
} from './lib/common';
import { scheduler } from './lib/scheduler'; import { scheduler } from './lib/scheduler';
import { compareVersions } from 'compare-versions'; import { compareVersions } from 'compare-versions';
import Graceful from '@ladjs/graceful' import Graceful from '@ladjs/graceful';
import yaml from 'js-yaml' import yaml from 'js-yaml';
import fs from 'fs/promises'; import fs from 'fs/promises';
import { verifyRemoteDockerEngineFn } from './routes/api/v1/destinations/handlers'; import { verifyRemoteDockerEngineFn } from './routes/api/v1/destinations/handlers';
import { checkContainer } from './lib/docker'; import { checkContainer } from './lib/docker';
@@ -23,13 +36,13 @@ import * as Sentry from '@sentry/node';
declare module 'fastify' { declare module 'fastify' {
interface FastifyInstance { interface FastifyInstance {
config: { config: {
COOLIFY_APP_ID: string, COOLIFY_APP_ID: string;
COOLIFY_SECRET_KEY: string, COOLIFY_SECRET_KEY: string;
COOLIFY_DATABASE_URL: string, COOLIFY_DATABASE_URL: string;
COOLIFY_IS_ON: string, COOLIFY_IS_ON: string;
COOLIFY_WHITE_LABELED: string, COOLIFY_WHITE_LABELED: string;
COOLIFY_WHITE_LABELED_ICON: string | null, COOLIFY_WHITE_LABELED_ICON: string | null;
COOLIFY_AUTO_UPDATE: string, COOLIFY_AUTO_UPDATE: string;
}; };
} }
} }
@@ -38,7 +51,7 @@ const port = isDev ? 3001 : 3000;
const host = '0.0.0.0'; const host = '0.0.0.0';
(async () => { (async () => {
const settings = await prisma.setting.findFirst() const settings = await prisma.setting.findFirst();
const fastify = Fastify({ const fastify = Fastify({
logger: settings?.isAPIDebuggingEnabled || false, logger: settings?.isAPIDebuggingEnabled || false,
trustProxy: true trustProxy: true
@@ -49,10 +62,10 @@ const host = '0.0.0.0';
required: ['COOLIFY_SECRET_KEY', 'COOLIFY_DATABASE_URL', 'COOLIFY_IS_ON'], required: ['COOLIFY_SECRET_KEY', 'COOLIFY_DATABASE_URL', 'COOLIFY_IS_ON'],
properties: { properties: {
COOLIFY_APP_ID: { COOLIFY_APP_ID: {
type: 'string', type: 'string'
}, },
COOLIFY_SECRET_KEY: { COOLIFY_SECRET_KEY: {
type: 'string', type: 'string'
}, },
COOLIFY_DATABASE_URL: { COOLIFY_DATABASE_URL: {
type: 'string', type: 'string',
@@ -73,8 +86,7 @@ const host = '0.0.0.0';
COOLIFY_AUTO_UPDATE: { COOLIFY_AUTO_UPDATE: {
type: 'string', type: 'string',
default: 'false' default: 'false'
}, }
} }
}; };
const options = { const options = {
@@ -103,13 +115,13 @@ const host = '0.0.0.0';
fastify.register(autoLoad, { fastify.register(autoLoad, {
dir: join(__dirname, 'routes') dir: join(__dirname, 'routes')
}); });
fastify.register(cookie) fastify.register(cookie);
fastify.register(cors); fastify.register(cors);
fastify.register(socketIO, { fastify.register(socketIO, {
cors: { cors: {
origin: isDev ? "*" : '' origin: isDev ? '*' : ''
} }
}) });
// To detect allowed origins // To detect allowed origins
// fastify.addHook('onRequest', async (request, reply) => { // fastify.addHook('onRequest', async (request, reply) => {
// console.log(request.headers.host) // console.log(request.headers.host)
@@ -131,10 +143,9 @@ const host = '0.0.0.0';
// } // }
// }) // })
try { try {
await fastify.listen({ port, host }) await fastify.listen({ port, host });
await socketIOServer(fastify) await socketIOServer(fastify);
console.log(`Coolify's API is listening on ${host}:${port}`); console.log(`Coolify's API is listening on ${host}:${port}`);
migrateServicesToNewTemplate(); migrateServicesToNewTemplate();
@@ -148,105 +159,100 @@ const host = '0.0.0.0';
if (!scheduler.workers.has('deployApplication')) { if (!scheduler.workers.has('deployApplication')) {
scheduler.run('deployApplication'); scheduler.run('deployApplication');
} }
}, 2000) }, 2000);
// autoUpdater // autoUpdater
setInterval(async () => { setInterval(async () => {
await autoUpdater() await autoUpdater();
}, 60000 * 15) }, 60000 * 15);
// cleanupStorage // cleanupStorage
setInterval(async () => { setInterval(async () => {
await cleanupStorage() await cleanupStorage();
}, 60000 * 10) }, 2000);
// checkProxies, checkFluentBit & refresh templates // checkProxies, checkFluentBit & refresh templates
setInterval(async () => { setInterval(async () => {
await checkProxies(); await checkProxies();
await checkFluentBit(); await checkFluentBit();
}, 60000) }, 60000);
// Refresh and check templates // Refresh and check templates
setInterval(async () => { setInterval(async () => {
await refreshTemplates() await refreshTemplates();
}, 60000) }, 60000);
setInterval(async () => { setInterval(async () => {
await refreshTags() await refreshTags();
}, 60000) }, 60000);
setInterval(async () => { setInterval(
await migrateServicesToNewTemplate() async () => {
}, isDev ? 10000 : 60000) await migrateServicesToNewTemplate();
},
isDev ? 10000 : 60000
);
setInterval(async () => { setInterval(async () => {
await copySSLCertificates(); await copySSLCertificates();
}, 10000) }, 10000);
await Promise.all([
getTagsTemplates(),
getArch(),
getIPAddress(),
configureRemoteDockers(),
])
await Promise.all([getTagsTemplates(), getArch(), getIPAddress(), configureRemoteDockers()]);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
process.exit(1); process.exit(1);
} }
})(); })();
async function getIPAddress() { async function getIPAddress() {
const { publicIpv4, publicIpv6 } = await import('public-ip') const { publicIpv4, publicIpv6 } = await import('public-ip');
try { try {
const settings = await listSettings(); const settings = await listSettings();
if (!settings.ipv4) { if (!settings.ipv4) {
const ipv4 = await publicIpv4({ timeout: 2000 }) const ipv4 = await publicIpv4({ timeout: 2000 });
console.log(`Getting public IPv4 address...`); console.log(`Getting public IPv4 address...`);
await prisma.setting.update({ where: { id: settings.id }, data: { ipv4 } }) await prisma.setting.update({ where: { id: settings.id }, data: { ipv4 } });
} }
if (!settings.ipv6) { if (!settings.ipv6) {
const ipv6 = await publicIpv6({ timeout: 2000 }) const ipv6 = await publicIpv6({ timeout: 2000 });
console.log(`Getting public IPv6 address...`); console.log(`Getting public IPv6 address...`);
await prisma.setting.update({ where: { id: settings.id }, data: { ipv6 } }) await prisma.setting.update({ where: { id: settings.id }, data: { ipv6 } });
} }
} catch (error) {}
} catch (error) { }
} }
async function getTagsTemplates() { async function getTagsTemplates() {
const { default: got } = await import('got') const { default: got } = await import('got');
try { try {
if (isDev) { if (isDev) {
const templates = await fs.readFile('./devTemplates.yaml', 'utf8') const templates = await fs.readFile('./devTemplates.yaml', 'utf8');
const tags = await fs.readFile('./devTags.json', 'utf8') const tags = await fs.readFile('./devTags.json', 'utf8');
await fs.writeFile('./templates.json', JSON.stringify(yaml.load(templates))) await fs.writeFile('./templates.json', JSON.stringify(yaml.load(templates)));
await fs.writeFile('./tags.json', tags) await fs.writeFile('./tags.json', tags);
console.log('[004] Tags and templates loaded in dev mode...') console.log('[004] Tags and templates loaded in dev mode...');
} else { } else {
const tags = await got.get('https://get.coollabs.io/coolify/service-tags.json').text() const tags = await got.get('https://get.coollabs.io/coolify/service-tags.json').text();
const response = await got.get('https://get.coollabs.io/coolify/service-templates.yaml').text() const response = await got
await fs.writeFile('/app/templates.json', JSON.stringify(yaml.load(response))) .get('https://get.coollabs.io/coolify/service-templates.yaml')
await fs.writeFile('/app/tags.json', tags) .text();
console.log('[004] Tags and templates loaded...') await fs.writeFile('/app/templates.json', JSON.stringify(yaml.load(response)));
await fs.writeFile('/app/tags.json', tags);
console.log('[004] Tags and templates loaded...');
} }
} catch (error) { } catch (error) {
console.log("Couldn't get latest templates.") console.log("Couldn't get latest templates.");
console.log(error) console.log(error);
} }
} }
async function initServer() { async function initServer() {
const appId = process.env['COOLIFY_APP_ID']; const appId = process.env['COOLIFY_APP_ID'];
const settings = await prisma.setting.findUnique({ where: { id: '0' } }) const settings = await prisma.setting.findUnique({ where: { id: '0' } });
try { try {
if (settings.doNotTrack === true) { if (settings.doNotTrack === true) {
console.log('[000] Telemetry disabled...') console.log('[000] Telemetry disabled...');
} else { } else {
if (settings.sentryDSN !== sentryDSN) { if (settings.sentryDSN !== sentryDSN) {
await prisma.setting.update({ where: { id: '0' }, data: { sentryDSN } }) await prisma.setting.update({ where: { id: '0' }, data: { sentryDSN } });
} }
// Initialize Sentry // Initialize Sentry
// Sentry.init({ // Sentry.init({
@@ -257,35 +263,38 @@ async function initServer() {
// console.log('[000] Sentry initialized...') // console.log('[000] Sentry initialized...')
} }
} catch (error) { } catch (error) {
console.error(error) console.error(error);
} }
try { try {
console.log(`[001] Initializing server...`); console.log(`[001] Initializing server...`);
await executeCommand({ command: `docker network create --attachable coolify` }); await executeCommand({ command: `docker network create --attachable coolify` });
} catch (error) { } } catch (error) {}
try { try {
console.log(`[002] Cleanup stucked builds...`); console.log(`[002] Cleanup stucked builds...`);
const isOlder = compareVersions('3.8.1', version); const isOlder = compareVersions('3.8.1', version);
if (isOlder === 1) { if (isOlder === 1) {
await prisma.build.updateMany({ where: { status: { in: ['running', 'queued'] } }, data: { status: 'failed' } }); await prisma.build.updateMany({
where: { status: { in: ['running', 'queued'] } },
data: { status: 'failed' }
});
} }
} catch (error) { } } catch (error) {}
try { try {
console.log('[003] Cleaning up old build sources under /tmp/build-sources/...'); console.log('[003] Cleaning up old build sources under /tmp/build-sources/...');
await fs.rm('/tmp/build-sources', { recursive: true, force: true }) await fs.rm('/tmp/build-sources', { recursive: true, force: true });
} catch (error) { } catch (error) {
console.log(error) console.log(error);
} }
} }
async function getArch() { async function getArch() {
try { try {
const settings = await prisma.setting.findFirst({}) const settings = await prisma.setting.findFirst({});
if (settings && !settings.arch) { if (settings && !settings.arch) {
console.log(`Getting architecture...`); console.log(`Getting architecture...`);
await prisma.setting.update({ where: { id: settings.id }, data: { arch: process.arch } }) await prisma.setting.update({ where: { id: settings.id }, data: { arch: process.arch } });
} }
} catch (error) { } } catch (error) {}
} }
async function configureRemoteDockers() { async function configureRemoteDockers() {
@@ -296,37 +305,44 @@ async function configureRemoteDockers() {
if (remoteDocker.length > 0) { if (remoteDocker.length > 0) {
console.log(`Verifying Remote Docker Engines...`); console.log(`Verifying Remote Docker Engines...`);
for (const docker of remoteDocker) { for (const docker of remoteDocker) {
console.log('Verifying:', docker.remoteIpAddress) console.log('Verifying:', docker.remoteIpAddress);
await verifyRemoteDockerEngineFn(docker.id); await verifyRemoteDockerEngineFn(docker.id);
} }
} }
} catch (error) { } catch (error) {
console.log(error) console.log(error);
} }
} }
async function autoUpdater() { async function autoUpdater() {
try { try {
const { default: got } = await import('got') const { default: got } = await import('got');
const currentVersion = version; const currentVersion = version;
const { coolify } = await got.get('https://get.coollabs.io/versions.json', { const { coolify } = await got
.get('https://get.coollabs.io/versions.json', {
searchParams: { searchParams: {
appId: process.env['COOLIFY_APP_ID'] || undefined, appId: process.env['COOLIFY_APP_ID'] || undefined,
version: currentVersion version: currentVersion
} }
}).json() })
.json();
const latestVersion = coolify.main.version; const latestVersion = coolify.main.version;
const isUpdateAvailable = compareVersions(latestVersion, currentVersion); const isUpdateAvailable = compareVersions(latestVersion, currentVersion);
if (isUpdateAvailable === 1) { if (isUpdateAvailable === 1) {
const activeCount = 0 const activeCount = 0;
if (activeCount === 0) { if (activeCount === 0) {
if (!isDev) { if (!isDev) {
const { isAutoUpdateEnabled } = await prisma.setting.findFirst(); const { isAutoUpdateEnabled } = await prisma.setting.findFirst();
if (isAutoUpdateEnabled) { if (isAutoUpdateEnabled) {
await executeCommand({ command: `docker pull coollabsio/coolify:${latestVersion}` }) await executeCommand({ command: `docker pull coollabsio/coolify:${latestVersion}` });
await executeCommand({ shell: true, command: `env | grep '^COOLIFY' > .env` }) await executeCommand({ shell: true, command: `env | grep '^COOLIFY' > .env` });
await executeCommand({ command: `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env` }) await executeCommand({
await executeCommand({ shell: true, command: `docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify coolify-fluentbit && docker rm coolify coolify-fluentbit && docker compose pull && docker compose up -d --force-recreate"` }) command: `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env`
});
await executeCommand({
shell: true,
command: `docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify coolify-fluentbit && docker rm coolify coolify-fluentbit && docker compose pull && docker compose up -d --force-recreate"`
});
} }
} else { } else {
console.log('Updating (not really in dev mode).'); console.log('Updating (not really in dev mode).');
@@ -334,7 +350,7 @@ async function autoUpdater() {
} }
} }
} catch (error) { } catch (error) {
console.log(error) console.log(error);
} }
} }
@@ -345,14 +361,18 @@ async function checkFluentBit() {
const { id } = await prisma.destinationDocker.findFirst({ const { id } = await prisma.destinationDocker.findFirst({
where: { engine, network: 'coolify' } where: { engine, network: 'coolify' }
}); });
const { found } = await checkContainer({ dockerId: id, container: 'coolify-fluentbit', remove: true }); const { found } = await checkContainer({
dockerId: id,
container: 'coolify-fluentbit',
remove: true
});
if (!found) { if (!found) {
await executeCommand({ shell: true, command: `env | grep '^COOLIFY' > .env` }); await executeCommand({ shell: true, command: `env | grep '^COOLIFY' > .env` });
await executeCommand({ command: `docker compose up -d fluent-bit` }); await executeCommand({ command: `docker compose up -d fluent-bit` });
} }
} }
} catch (error) { } catch (error) {
console.log(error) console.log(error);
} }
} }
async function checkProxies() { async function checkProxies() {
@@ -368,7 +388,7 @@ async function checkProxies() {
where: { engine, network: 'coolify', isCoolifyProxyUsed: true } where: { engine, network: 'coolify', isCoolifyProxyUsed: true }
}); });
if (localDocker) { if (localDocker) {
portReachable = await isReachable(80, { host: ipv4 || ipv6 }) portReachable = await isReachable(80, { host: ipv4 || ipv6 });
if (!portReachable) { if (!portReachable) {
await startTraefikProxy(localDocker.id); await startTraefikProxy(localDocker.id);
} }
@@ -380,14 +400,14 @@ async function checkProxies() {
if (remoteDocker.length > 0) { if (remoteDocker.length > 0) {
for (const docker of remoteDocker) { for (const docker of remoteDocker) {
if (docker.isCoolifyProxyUsed) { if (docker.isCoolifyProxyUsed) {
portReachable = await isReachable(80, { host: docker.remoteIpAddress }) portReachable = await isReachable(80, { host: docker.remoteIpAddress });
if (!portReachable) { if (!portReachable) {
await startTraefikProxy(docker.id); await startTraefikProxy(docker.id);
} }
} }
try { try {
await createRemoteEngineConfiguration(docker.id) await createRemoteEngineConfiguration(docker.id);
} catch (error) { } } catch (error) {}
} }
} }
// TCP Proxies // TCP Proxies
@@ -426,80 +446,105 @@ async function checkProxies() {
// await startTraefikTCPProxy(destinationDocker, id, publicPort, 9000); // await startTraefikTCPProxy(destinationDocker, id, publicPort, 9000);
// } // }
// } // }
} catch (error) { } catch (error) {}
}
} }
async function copySSLCertificates() { async function copySSLCertificates() {
try { try {
const pAll = await import('p-all'); const pAll = await import('p-all');
const actions = [] const actions = [];
const certificates = await prisma.certificate.findMany({ include: { team: true } }) const certificates = await prisma.certificate.findMany({ include: { team: true } });
const teamIds = certificates.map(c => c.teamId) const teamIds = certificates.map((c) => c.teamId);
const destinations = await prisma.destinationDocker.findMany({ where: { isCoolifyProxyUsed: true, teams: { some: { id: { in: [...teamIds] } } } } }) const destinations = await prisma.destinationDocker.findMany({
where: { isCoolifyProxyUsed: true, teams: { some: { id: { in: [...teamIds] } } } }
});
for (const certificate of certificates) { for (const certificate of certificates) {
const { id, key, cert } = certificate const { id, key, cert } = certificate;
const decryptedKey = decrypt(key) const decryptedKey = decrypt(key);
await fs.writeFile(`/tmp/${id}-key.pem`, decryptedKey) await fs.writeFile(`/tmp/${id}-key.pem`, decryptedKey);
await fs.writeFile(`/tmp/${id}-cert.pem`, cert) await fs.writeFile(`/tmp/${id}-cert.pem`, cert);
for (const destination of destinations) { for (const destination of destinations) {
if (destination.remoteEngine) { if (destination.remoteEngine) {
if (destination.remoteVerified) { if (destination.remoteVerified) {
const { id: dockerId, remoteIpAddress } = destination const { id: dockerId, remoteIpAddress } = destination;
actions.push(async () => copyRemoteCertificates(id, dockerId, remoteIpAddress)) actions.push(async () => copyRemoteCertificates(id, dockerId, remoteIpAddress));
} }
} else { } else {
actions.push(async () => copyLocalCertificates(id)) actions.push(async () => copyLocalCertificates(id));
} }
} }
} }
await pAll.default(actions, { concurrency: 1 }) await pAll.default(actions, { concurrency: 1 });
} catch (error) { } catch (error) {
console.log(error) console.log(error);
} finally { } finally {
await executeCommand({ command: `find /tmp/ -maxdepth 1 -type f -name '*-*.pem' -delete` }) await executeCommand({ command: `find /tmp/ -maxdepth 1 -type f -name '*-*.pem' -delete` });
} }
} }
async function copyRemoteCertificates(id: string, dockerId: string, remoteIpAddress: string) { async function copyRemoteCertificates(id: string, dockerId: string, remoteIpAddress: string) {
try { try {
await executeCommand({ command: `scp /tmp/${id}-cert.pem /tmp/${id}-key.pem ${remoteIpAddress}:/tmp/` }) await executeCommand({
await executeCommand({ sshCommand: true, shell: true, dockerId, command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'` }) command: `scp /tmp/${id}-cert.pem /tmp/${id}-key.pem ${remoteIpAddress}:/tmp/`
await executeCommand({ sshCommand: true, dockerId, command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/` }) });
await executeCommand({ sshCommand: true, dockerId, command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/` }) await executeCommand({
sshCommand: true,
shell: true,
dockerId,
command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'`
});
await executeCommand({
sshCommand: true,
dockerId,
command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/`
});
await executeCommand({
sshCommand: true,
dockerId,
command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/`
});
} catch (error) { } catch (error) {
console.log({ error }) console.log({ error });
} }
} }
async function copyLocalCertificates(id: string) { async function copyLocalCertificates(id: string) {
try { try {
await executeCommand({ command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'`, shell: true }) await executeCommand({
await executeCommand({ command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/` }) command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'`,
await executeCommand({ command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/` }) shell: true
});
await executeCommand({
command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/`
});
await executeCommand({
command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/`
});
} catch (error) { } catch (error) {
console.log({ error }) console.log({ error });
} }
} }
async function cleanupStorage() { async function cleanupStorage() {
const destinationDockers = await prisma.destinationDocker.findMany(); const destinationDockers = await prisma.destinationDocker.findMany();
let enginesDone = new Set() let enginesDone = new Set();
for (const destination of destinationDockers) { for (const destination of destinationDockers) {
if (enginesDone.has(destination.engine) || enginesDone.has(destination.remoteIpAddress)) return if (enginesDone.has(destination.engine) || enginesDone.has(destination.remoteIpAddress)) return;
if (destination.engine) enginesDone.add(destination.engine) if (destination.engine) enginesDone.add(destination.engine);
if (destination.remoteIpAddress) enginesDone.add(destination.remoteIpAddress) if (destination.remoteIpAddress) enginesDone.add(destination.remoteIpAddress);
let force = false;
let lowDiskSpace = false; let lowDiskSpace = false;
try { try {
let stdout = null let stdout = null;
if (!isDev) { if (!isDev) {
const output = await executeCommand({ dockerId: destination.id, command: `CONTAINER=$(docker ps -lq | head -1) && docker exec $CONTAINER sh -c 'df -kPT /'`, shell: true }) const output = await executeCommand({
dockerId: destination.id,
command: `CONTAINER=$(docker ps -lq | head -1) && docker exec $CONTAINER sh -c 'df -kPT /'`,
shell: true
});
stdout = output.stdout; stdout = output.stdout;
} else { } else {
const output = await executeCommand({ const output = await executeCommand({
command: command: `df -kPT /`
`df -kPT /`
}); });
stdout = output.stdout; stdout = output.stdout;
} }
@@ -529,9 +574,10 @@ async function cleanupStorage() {
const { capacity } = data[0]; const { capacity } = data[0];
if (capacity > 0.8) { if (capacity > 0.8) {
lowDiskSpace = true; lowDiskSpace = true;
force = true;
} }
} }
} catch (error) { } } catch (error) {}
await cleanupDockerStorage(destination.id, lowDiskSpace, false) await cleanupDockerStorage(destination.id, lowDiskSpace, force);
} }
} }

View File

@@ -19,7 +19,7 @@ import { saveBuildLog, saveDockerRegistryCredentials } from './buildPacks/common
import { scheduler } from './scheduler'; import { scheduler } from './scheduler';
import type { ExecaChildProcess } from 'execa'; import type { ExecaChildProcess } from 'execa';
export const version = '3.12.4'; export const version = '3.12.5';
export const isDev = process.env.NODE_ENV === 'development'; export const isDev = process.env.NODE_ENV === 'development';
export const sentryDSN = export const sentryDSN =
'https://409f09bcb7af47928d3e0f46b78987f3@o1082494.ingest.sentry.io/4504236622217216'; 'https://409f09bcb7af47928d3e0f46b78987f3@o1082494.ingest.sentry.io/4504236622217216';
@@ -584,7 +584,7 @@ export async function executeCommand({
} }
if (sshCommand) { if (sshCommand) {
if (shell) { if (shell) {
return execaCommand(`ssh ${remoteIpAddress}-remote ${command}`); return execaCommand(`ssh ${remoteIpAddress}-remote ${command}`, { shell: true });
} }
return await execa('ssh', [`${remoteIpAddress}-remote`, dockerCommand, ...dockerArgs]); return await execa('ssh', [`${remoteIpAddress}-remote`, dockerCommand, ...dockerArgs]);
} }
@@ -651,11 +651,13 @@ export async function executeCommand({
} else { } else {
if (shell) { if (shell) {
return await execaCommand(command, { return await execaCommand(command, {
env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine } env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine },
shell: true
}); });
} else { } else {
return await execa(dockerCommand, dockerArgs, { return await execa(dockerCommand, dockerArgs, {
env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine } env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine },
shell: false
}); });
} }
} }
@@ -1751,6 +1753,10 @@ export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
let keepImage = []; let keepImage = [];
for (const image2 of imagesArray) { for (const image2 of imagesArray) {
if (image2.startsWith(image)) { if (image2.startsWith(image)) {
if (force) {
deleteImage.push(image2);
continue;
}
if (keepImage.length >= numberOfDockerImagesKeptLocally) { if (keepImage.length >= numberOfDockerImagesKeptLocally) {
deleteImage.push(image2); deleteImage.push(image2);
} else { } else {
@@ -1760,7 +1766,11 @@ export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
} }
} }
for (const image of deleteImage) { for (const image of deleteImage) {
try {
await executeCommand({ dockerId, command: `docker image rm -f ${image}` }); await executeCommand({ dockerId, command: `docker image rm -f ${image}` });
} catch (error) {
console.log(error);
}
} }
// Prune coolify managed containers // Prune coolify managed containers

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
{ {
"name": "coolify", "name": "coolify",
"description": "An open-source & self-hostable Heroku / Netlify alternative.", "description": "An open-source & self-hostable Heroku / Netlify alternative.",
"version": "3.12.4", "version": "3.12.5",
"license": "Apache-2.0", "license": "Apache-2.0",
"repository": "github:coollabsio/coolify", "repository": "github:coollabsio/coolify",
"scripts": { "scripts": {