From 531c712ea53791eef60136c34aabe1771fddeffa Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 15 Feb 2022 21:44:36 +0100 Subject: [PATCH] fix: Some nasty bug fix: Automatic reconfiguration of all services and service proxies --- src/lib/buildPacks/common.ts | 20 +---- src/lib/database/common.ts | 40 +++++++-- src/lib/haproxy/index.ts | 82 +++++++++-------- src/lib/queues/proxy.ts | 87 +++++++++++++------ .../destinations/[id]/_LocalDocker.svelte | 27 +++--- src/routes/services/[id]/minio/start.json.ts | 4 +- src/routes/services/[id]/nocodb/start.json.ts | 4 +- .../[id]/plausibleanalytics/start.json.ts | 5 +- .../services/[id]/vaultwarden/start.json.ts | 4 +- .../services/[id]/vscodeserver/start.json.ts | 4 +- .../services/[id]/wordpress/start.json.ts | 4 +- 11 files changed, 181 insertions(+), 100 deletions(-) diff --git a/src/lib/buildPacks/common.ts b/src/lib/buildPacks/common.ts index a156864df..67be90ee5 100644 --- a/src/lib/buildPacks/common.ts +++ b/src/lib/buildPacks/common.ts @@ -74,26 +74,12 @@ export async function makeLabelForStandaloneDatabase({ id, image, volume }) { ]; } -export async function makeLabelForPlausibleAnalytics({ id, images, volume }) { - const service = await db.prisma.service.findFirst({ - where: { id }, - include: { plausibleAnalytics: true } - }); - delete service.destinationDockerId; - delete service.createdAt; - delete service.updatedAt; +export function makeLabelForServices(type) { return [ 'coolify.managed=true', `coolify.version=${version}`, - `coolify.type=service-plausibleanalytics`, - `coolify.configuration=${base64Encode( - JSON.stringify({ - version, - images, - volume, - ...service - }) - )}` + `coolify.type=service`, + `coolify.service.type=${type}` ]; } diff --git a/src/lib/database/common.ts b/src/lib/database/common.ts index cbbc55624..290ecd2b5 100644 --- a/src/lib/database/common.ts +++ b/src/lib/database/common.ts @@ -114,27 +114,55 @@ export const supportedServiceTypesAndVersions = [ name: 'plausibleanalytics', fancyName: 'Plausible Analytics', baseImage: 'plausible/analytics', - versions: ['latest'] + versions: ['latest'], + ports: { + main: 8000 + } + }, + { + name: 'nocodb', + fancyName: 'NocoDB', + baseImage: 'nocodb/nocodb', + versions: ['latest'], + ports: { + main: 8080 + } + }, + { + name: 'minio', + fancyName: 'MinIO', + baseImage: 'minio/minio', + versions: ['latest'], + ports: { + main: 9001 + } }, - { name: 'nocodb', fancyName: 'NocoDB', baseImage: 'nocodb/nocodb', versions: ['latest'] }, - { name: 'minio', fancyName: 'MinIO', baseImage: 'minio/minio', versions: ['latest'] }, { name: 'vscodeserver', fancyName: 'VSCode Server', baseImage: 'codercom/code-server', - versions: ['latest'] + versions: ['latest'], + ports: { + main: 8080 + } }, { name: 'wordpress', fancyName: 'Wordpress', baseImage: 'wordpress', - versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3'] + versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3'], + ports: { + main: 80 + } }, { name: 'vaultwarden', fancyName: 'Vaultwarden', baseImage: 'vaultwarden/server', - versions: ['latest'] + versions: ['latest'], + ports: { + main: 80 + } } ]; diff --git a/src/lib/haproxy/index.ts b/src/lib/haproxy/index.ts index 954e72f6a..0eaee0109 100644 --- a/src/lib/haproxy/index.ts +++ b/src/lib/haproxy/index.ts @@ -509,46 +509,60 @@ export async function configureNetworkCoolifyProxy(engine) { export async function configureSimpleServiceProxyOn({ id, domain, port }) { const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); + let serverConfigured = false; + let backendAvailable: any = null; + try { - await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json(); - const transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/backends/${domain}`, { + backendAvailable = await haproxy + .get(`v2/services/haproxy/configuration/backends/${domain}`) + .json(); + const server: any = await haproxy + .get(`v2/services/haproxy/configuration/servers/${id}`, { searchParams: { - transaction_id: transactionId + backend: domain } }) .json(); - await completeTransaction(transactionId); + if (backendAvailable && server) { + // Very sophisticated way to check if the server is already configured in proxy + if (backendAvailable.data.forwardfor.enabled === 'enabled') { + if (backendAvailable.data.name === domain) { + if (server.data.check === 'enabled') { + if (server.data.address === id) { + if (server.data.port === port) { + serverConfigured = true; + } + } + } + } + } + } } catch (error) {} - try { - const transactionId = await getNextTransactionId(); - await haproxy.post('v2/services/haproxy/configuration/backends', { - searchParams: { - transaction_id: transactionId - }, - json: { - 'init-addr': 'last,libc,none', - forwardfor: { enabled: 'enabled' }, - name: domain - } - }); - await haproxy.post('v2/services/haproxy/configuration/servers', { - searchParams: { - transaction_id: transactionId, - backend: domain - }, - json: { - address: id, - check: 'enabled', - name: id, - port: port - } - }); - await completeTransaction(transactionId); - } catch (error) { - console.log(error); - } + if (serverConfigured) return; + const transactionId = await getNextTransactionId(); + await haproxy.post('v2/services/haproxy/configuration/backends', { + searchParams: { + transaction_id: transactionId + }, + json: { + 'init-addr': 'last,libc,none', + forwardfor: { enabled: 'enabled' }, + name: domain + } + }); + await haproxy.post('v2/services/haproxy/configuration/servers', { + searchParams: { + transaction_id: transactionId, + backend: domain + }, + json: { + address: id, + check: 'enabled', + name: id, + port: port + } + }); + await completeTransaction(transactionId); } export async function configureSimpleServiceProxyOff({ domain }) { diff --git a/src/lib/queues/proxy.ts b/src/lib/queues/proxy.ts index ea7b3c142..92d14f738 100644 --- a/src/lib/queues/proxy.ts +++ b/src/lib/queues/proxy.ts @@ -1,14 +1,16 @@ import { getDomain } from '$lib/common'; -import { getApplicationById, prisma } from '$lib/database'; +import { getApplicationById, prisma, supportedServiceTypesAndVersions } from '$lib/database'; import { dockerInstance } from '$lib/docker'; import { checkContainer, configureCoolifyProxyOn, configureProxyForApplication, + configureSimpleServiceProxyOn, forceSSLOnApplication, reloadHaproxy, setWwwRedirection, - startCoolifyProxy + startCoolifyProxy, + startHttpProxy } from '$lib/haproxy'; import * as db from '$lib/database'; @@ -24,35 +26,70 @@ export default async function () { (container) => container.Labels['coolify.managed'] ); for (const configuration of configurations) { - const parsedConfiguration = JSON.parse( - Buffer.from(configuration.Labels['coolify.configuration'], 'base64').toString() - ); - if ( - parsedConfiguration && - configuration.Labels['coolify.type'] === 'standalone-application' - ) { - const { fqdn, applicationId, port, pullmergeRequestId } = parsedConfiguration; - if (fqdn) { - const found = await getApplicationById({ id: applicationId }); - if (found) { - const domain = getDomain(fqdn); - await configureProxyForApplication({ - domain, - imageId: pullmergeRequestId - ? `${applicationId}-${pullmergeRequestId}` - : applicationId, - applicationId, - port - }); - const isHttps = fqdn.startsWith('https://'); - if (isHttps) await forceSSLOnApplication({ domain }); - await setWwwRedirection(fqdn); + if (configuration.Labels['coolify.configuration']) { + const parsedConfiguration = JSON.parse( + Buffer.from(configuration.Labels['coolify.configuration'], 'base64').toString() + ); + if ( + parsedConfiguration && + configuration.Labels['coolify.type'] === 'standalone-application' + ) { + const { fqdn, applicationId, port, pullmergeRequestId } = parsedConfiguration; + if (fqdn) { + const found = await getApplicationById({ id: applicationId }); + if (found) { + const domain = getDomain(fqdn); + await configureProxyForApplication({ + domain, + imageId: pullmergeRequestId + ? `${applicationId}-${pullmergeRequestId}` + : applicationId, + applicationId, + port + }); + const isHttps = fqdn.startsWith('https://'); + if (isHttps) await forceSSLOnApplication({ domain }); + await setWwwRedirection(fqdn); + } + } + } + } + } + for (const container of containers) { + const image = container.Image.split(':')[0]; + const found = supportedServiceTypesAndVersions.find((a) => a.baseImage === image); + if (found) { + const type = found.name; + const mainPort = found.ports.main; + const id = container.Names[0].replace('/', ''); + const service = await db.prisma.service.findUnique({ + where: { id }, + include: { + destinationDocker: true, + minio: true, + plausibleAnalytics: true, + vscodeserver: true, + wordpress: true + } + }); + const { fqdn } = service; + const domain = getDomain(fqdn); + await configureSimpleServiceProxyOn({ id, domain, port: mainPort }); + const publicPort = service[type]?.publicPort; + if (publicPort) { + const containerFound = await checkContainer( + destination.engine, + `haproxy-for-${publicPort}` + ); + if (!containerFound) { + await startHttpProxy(destination, id, publicPort, 9000); } } } } } } + const services = await prisma.service.findMany({}); // Check Coolify FQDN and configure proxy if needed const { fqdn } = await db.listSettings(); if (fqdn) { diff --git a/src/routes/destinations/[id]/_LocalDocker.svelte b/src/routes/destinations/[id]/_LocalDocker.svelte index a25af53bd..4d584e90a 100644 --- a/src/routes/destinations/[id]/_LocalDocker.svelte +++ b/src/routes/destinations/[id]/_LocalDocker.svelte @@ -102,17 +102,22 @@ } } async function forceRestartProxy() { - try { - restarting = true; - toast.push('Coolify Proxy restarting...'); - await post(`/destinations/${id}/restart.json`, { - engine: destination.engine, - fqdn: settings.fqdn - }); - } catch ({ error }) { - setTimeout(() => { - window.location.reload(); - }, 5000); + const sure = confirm( + 'Are you sure you want to restart the proxy? Everyting will be reconfigured in ~10 sec.' + ); + if (sure) { + try { + restarting = true; + toast.push('Coolify Proxy restarting...'); + await post(`/destinations/${id}/restart.json`, { + engine: destination.engine, + fqdn: settings.fqdn + }); + } catch ({ error }) { + setTimeout(() => { + window.location.reload(); + }, 5000); + } } } diff --git a/src/routes/services/[id]/minio/start.json.ts b/src/routes/services/[id]/minio/start.json.ts index c08c18e54..e865b5e52 100644 --- a/src/routes/services/[id]/minio/start.json.ts +++ b/src/routes/services/[id]/minio/start.json.ts @@ -15,6 +15,7 @@ import { import getPort from 'get-port'; import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; +import { makeLabelForServices } from '$lib/buildPacks/common'; export const post: RequestHandler = async (event) => { const { teamId, status, body } = await getUserDetails(event); @@ -63,7 +64,8 @@ export const post: RequestHandler = async (event) => { environment: config.environmentVariables, networks: [network], volumes: [config.volume], - restart: 'always' + restart: 'always', + labels: makeLabelForServices('minio') } }, networks: { diff --git a/src/routes/services/[id]/nocodb/start.json.ts b/src/routes/services/[id]/nocodb/start.json.ts index 3ce559e45..e988c7eb4 100644 --- a/src/routes/services/[id]/nocodb/start.json.ts +++ b/src/routes/services/[id]/nocodb/start.json.ts @@ -12,6 +12,7 @@ import { } from '$lib/haproxy'; import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; +import { makeLabelForServices } from '$lib/buildPacks/common'; export const post: RequestHandler = async (event) => { const { teamId, status, body } = await getUserDetails(event); @@ -39,7 +40,8 @@ export const post: RequestHandler = async (event) => { container_name: id, image: `nocodb/nocodb:${version}`, networks: [network], - restart: 'always' + restart: 'always', + labels: makeLabelForServices('nocodb') } }, networks: { diff --git a/src/routes/services/[id]/plausibleanalytics/start.json.ts b/src/routes/services/[id]/plausibleanalytics/start.json.ts index 96dbac3c2..274c59bd9 100644 --- a/src/routes/services/[id]/plausibleanalytics/start.json.ts +++ b/src/routes/services/[id]/plausibleanalytics/start.json.ts @@ -12,6 +12,7 @@ import { } from '$lib/haproxy'; import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; +import { makeLabelForServices } from '$lib/buildPacks/common'; export const post: RequestHandler = async (event) => { const { teamId, status, body } = await getUserDetails(event); @@ -82,7 +83,6 @@ export const post: RequestHandler = async (event) => { const network = destinationDockerId && destinationDocker.network; const host = getEngine(destinationDocker.engine); const engine = destinationDocker.engine; - // const labels = await makeLabelForPlausibleAnalytics({ id, }) const { workdir } = await createDirectories({ repository: type, buildId: id }); @@ -138,7 +138,8 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`; environment: config.plausibleAnalytics.environmentVariables, volumes: [config.postgresql.volume], restart: 'always', - depends_on: [`${id}-postgresql`, `${id}-clickhouse`] + depends_on: [`${id}-postgresql`, `${id}-clickhouse`], + labels: makeLabelForServices('plausibleAnalytics') }, [`${id}-postgresql`]: { container_name: `${id}-postgresql`, diff --git a/src/routes/services/[id]/vaultwarden/start.json.ts b/src/routes/services/[id]/vaultwarden/start.json.ts index ab05590a8..4b6e3637a 100644 --- a/src/routes/services/[id]/vaultwarden/start.json.ts +++ b/src/routes/services/[id]/vaultwarden/start.json.ts @@ -12,6 +12,7 @@ import { } from '$lib/haproxy'; import { getDomain } from '$lib/components/common'; import { getServiceImage, ErrorHandler } from '$lib/database'; +import { makeLabelForServices } from '$lib/buildPacks/common'; export const post: RequestHandler = async (event) => { const { teamId, status, body } = await getUserDetails(event); @@ -46,7 +47,8 @@ export const post: RequestHandler = async (event) => { image: config.image, networks: [network], volumes: [config.volume], - restart: 'always' + restart: 'always', + labels: makeLabelForServices('vaultWarden') } }, networks: { diff --git a/src/routes/services/[id]/vscodeserver/start.json.ts b/src/routes/services/[id]/vscodeserver/start.json.ts index 4709a823e..fb49b1b7a 100644 --- a/src/routes/services/[id]/vscodeserver/start.json.ts +++ b/src/routes/services/[id]/vscodeserver/start.json.ts @@ -12,6 +12,7 @@ import { } from '$lib/haproxy'; import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; +import { makeLabelForServices } from '$lib/buildPacks/common'; export const post: RequestHandler = async (event) => { const { teamId, status, body } = await getUserDetails(event); @@ -54,7 +55,8 @@ export const post: RequestHandler = async (event) => { environment: config.environmentVariables, networks: [network], volumes: [config.volume], - restart: 'always' + restart: 'always', + labels: makeLabelForServices('vscodeServer') } }, networks: { diff --git a/src/routes/services/[id]/wordpress/start.json.ts b/src/routes/services/[id]/wordpress/start.json.ts index 2ed3039cd..e7c305deb 100644 --- a/src/routes/services/[id]/wordpress/start.json.ts +++ b/src/routes/services/[id]/wordpress/start.json.ts @@ -12,6 +12,7 @@ import { } from '$lib/haproxy'; import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; +import { makeLabelForServices } from '$lib/buildPacks/common'; export const post: RequestHandler = async (event) => { const { teamId, status, body } = await getUserDetails(event); @@ -78,7 +79,8 @@ export const post: RequestHandler = async (event) => { environment: config.wordpress.environmentVariables, networks: [network], restart: 'always', - depends_on: [`${id}-mysql`] + depends_on: [`${id}-mysql`], + labels: makeLabelForServices('wordpress') }, [`${id}-mysql`]: { container_name: `${id}-mysql`,