fix: verify and configure remote docker engines
This commit is contained in:
parent
a3af21275a
commit
bd27afe0da
apps
api/src
ui/src/routes/destinations/[id]
@ -10,6 +10,7 @@ import { asyncExecShell, createRemoteEngineConfiguration, getDomain, isDev, list
|
|||||||
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 { verifyRemoteDockerEngineFn } from './routes/api/v1/destinations/handlers';
|
||||||
declare module 'fastify' {
|
declare module 'fastify' {
|
||||||
interface FastifyInstance {
|
interface FastifyInstance {
|
||||||
config: {
|
config: {
|
||||||
@ -27,7 +28,8 @@ declare module 'fastify' {
|
|||||||
|
|
||||||
const port = isDev ? 3001 : 3000;
|
const port = isDev ? 3001 : 3000;
|
||||||
const host = '0.0.0.0';
|
const host = '0.0.0.0';
|
||||||
prisma.setting.findFirst().then(async (settings) => {
|
(async () => {
|
||||||
|
const settings = prisma.setting.findFirst()
|
||||||
const fastify = Fastify({
|
const fastify = Fastify({
|
||||||
logger: settings?.isAPIDebuggingEnabled || false,
|
logger: settings?.isAPIDebuggingEnabled || false,
|
||||||
trustProxy: true
|
trustProxy: true
|
||||||
@ -117,11 +119,8 @@ prisma.setting.findFirst().then(async (settings) => {
|
|||||||
// console.log('not allowed', request.headers.host)
|
// console.log('not allowed', request.headers.host)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
fastify.listen({ port, host }, async (err: any, address: any) => {
|
try {
|
||||||
if (err) {
|
await fastify.listen({ port, host })
|
||||||
console.error(err);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
console.log(`Coolify's API is listening on ${host}:${port}`);
|
console.log(`Coolify's API is listening on ${host}:${port}`);
|
||||||
await initServer();
|
await initServer();
|
||||||
|
|
||||||
@ -162,19 +161,28 @@ prisma.setting.findFirst().then(async (settings) => {
|
|||||||
getIPAddress(),
|
getIPAddress(),
|
||||||
configureRemoteDockers(),
|
configureRemoteDockers(),
|
||||||
])
|
])
|
||||||
});
|
} catch (error) {
|
||||||
})
|
console.error(error);
|
||||||
|
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) {
|
||||||
|
console.log(`Getting public IPv4 address...`);
|
||||||
const ipv4 = await publicIpv4({ timeout: 2000 })
|
const ipv4 = await publicIpv4({ timeout: 2000 })
|
||||||
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) {
|
||||||
|
console.log(`Getting public IPv6 address...`);
|
||||||
const ipv6 = await publicIpv6({ timeout: 2000 })
|
const ipv6 = await publicIpv6({ timeout: 2000 })
|
||||||
await prisma.setting.update({ where: { id: settings.id }, data: { ipv6 } })
|
await prisma.setting.update({ where: { id: settings.id }, data: { ipv6 } })
|
||||||
}
|
}
|
||||||
@ -183,6 +191,7 @@ async function getIPAddress() {
|
|||||||
}
|
}
|
||||||
async function initServer() {
|
async function initServer() {
|
||||||
try {
|
try {
|
||||||
|
console.log(`Initializing server...`);
|
||||||
await asyncExecShell(`docker network create --attachable coolify`);
|
await asyncExecShell(`docker network create --attachable coolify`);
|
||||||
} catch (error) { }
|
} catch (error) { }
|
||||||
try {
|
try {
|
||||||
@ -196,6 +205,7 @@ 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...`);
|
||||||
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) { }
|
||||||
@ -207,9 +217,13 @@ async function configureRemoteDockers() {
|
|||||||
where: { remoteVerified: true, remoteEngine: true }
|
where: { remoteVerified: true, remoteEngine: true }
|
||||||
});
|
});
|
||||||
if (remoteDocker.length > 0) {
|
if (remoteDocker.length > 0) {
|
||||||
|
console.log(`Verifying Remote Docker Engines...`);
|
||||||
for (const docker of remoteDocker) {
|
for (const docker of remoteDocker) {
|
||||||
await createRemoteEngineConfiguration(docker.id)
|
console.log('Verifying:', docker.remoteIpAddress)
|
||||||
|
await verifyRemoteDockerEngineFn(docker.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) { }
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import sshConfig from 'ssh-config'
|
|||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
|
|
||||||
import { asyncExecShell, createRemoteEngineConfiguration, decrypt, errorHandler, executeDockerCmd, listSettings, prisma, startTraefikProxy, stopTraefikProxy } from '../../../../lib/common';
|
import { asyncExecShell, createRemoteEngineConfiguration, decrypt, errorHandler, executeDockerCmd, executeSSHCmd, listSettings, prisma, startTraefikProxy, stopTraefikProxy } from '../../../../lib/common';
|
||||||
import { checkContainer } from '../../../../lib/docker';
|
import { checkContainer } from '../../../../lib/docker';
|
||||||
|
|
||||||
import type { OnlyId } from '../../../../types';
|
import type { OnlyId } from '../../../../types';
|
||||||
@ -202,25 +202,44 @@ export async function assignSSHKey(request: FastifyRequest) {
|
|||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function verifyRemoteDockerEngine(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
export async function verifyRemoteDockerEngineFn(id: string) {
|
||||||
|
await createRemoteEngineConfiguration(id);
|
||||||
|
const { remoteIpAddress, remoteUser, network, isCoolifyProxyUsed } = await prisma.destinationDocker.findFirst({ where: { id } })
|
||||||
|
const host = `ssh://${remoteUser}@${remoteIpAddress}`
|
||||||
|
const { stdout } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=${network}' --no-trunc --format "{{json .}}"`);
|
||||||
|
if (!stdout) {
|
||||||
|
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable ${network}`);
|
||||||
|
}
|
||||||
|
const { stdout: coolifyNetwork } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=coolify-infra' --no-trunc --format "{{json .}}"`);
|
||||||
|
if (!coolifyNetwork) {
|
||||||
|
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable coolify-infra`);
|
||||||
|
}
|
||||||
|
if (isCoolifyProxyUsed) await startTraefikProxy(id);
|
||||||
|
const { stdout: daemonJson } = await executeSSHCmd({ dockerId: id, command: `cat /etc/docker/daemon.json` });
|
||||||
try {
|
try {
|
||||||
const { id } = request.params;
|
let daemonJsonParsed = JSON.parse(daemonJson);
|
||||||
await createRemoteEngineConfiguration(id);
|
if (!daemonJsonParsed['live-restore'] || daemonJsonParsed['live-restore'] !== true) {
|
||||||
const { remoteIpAddress, remoteUser, network, isCoolifyProxyUsed } = await prisma.destinationDocker.findFirst({ where: { id } })
|
daemonJsonParsed['live-restore'] = true
|
||||||
const host = `ssh://${remoteUser}@${remoteIpAddress}`
|
await executeSSHCmd({ dockerId: id, command: `echo '${JSON.stringify(daemonJsonParsed)}' > /etc/docker/daemon.json` });
|
||||||
const { stdout } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=${network}' --no-trunc --format "{{json .}}"`);
|
await executeSSHCmd({ dockerId: id, command: `systemctl restart docker` });
|
||||||
if (!stdout) {
|
|
||||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable ${network}`);
|
|
||||||
}
|
}
|
||||||
const { stdout: coolifyNetwork } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=coolify-infra' --no-trunc --format "{{json .}}"`);
|
} catch (error) {
|
||||||
if (!coolifyNetwork) {
|
const daemonJsonParsed = {
|
||||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable coolify-infra`);
|
"live-restore": true
|
||||||
}
|
}
|
||||||
if (isCoolifyProxyUsed) await startTraefikProxy(id);
|
console.log(JSON.stringify(daemonJsonParsed))
|
||||||
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: true } })
|
await executeSSHCmd({ dockerId: id, command: `echo '${JSON.stringify(daemonJsonParsed)}' > /etc/docker/daemon.json` });
|
||||||
|
await executeSSHCmd({ dockerId: id, command: `systemctl restart docker` });
|
||||||
|
}
|
||||||
|
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: true } })
|
||||||
|
}
|
||||||
|
export async function verifyRemoteDockerEngine(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
||||||
|
const { id } = request.params;
|
||||||
|
try {
|
||||||
|
await verifyRemoteDockerEngineFn(id);
|
||||||
return reply.code(201).send()
|
return reply.code(201).send()
|
||||||
|
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
|
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: false } })
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,16 @@ export async function listServers(request: FastifyRequest) {
|
|||||||
try {
|
try {
|
||||||
const userId = request.user.userId;
|
const userId = request.user.userId;
|
||||||
const teamId = request.user.teamId;
|
const teamId = request.user.teamId;
|
||||||
const servers = await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId === '0' ? undefined : teamId } }}, distinct: ['remoteIpAddress', 'engine'] })
|
let servers = await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, distinct: ['remoteIpAddress', 'engine'] })
|
||||||
|
servers = servers.filter((server) => {
|
||||||
|
if (server.remoteEngine) {
|
||||||
|
if (server.remoteVerified) {
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
})
|
||||||
return {
|
return {
|
||||||
servers
|
servers
|
||||||
}
|
}
|
||||||
@ -78,7 +87,7 @@ export async function showUsage(request: FastifyRequest) {
|
|||||||
freeMemPercentage: (parsed.totalMemoryKB - parsed.usedMemoryKB) / parsed.totalMemoryKB * 100
|
freeMemPercentage: (parsed.totalMemoryKB - parsed.usedMemoryKB) / parsed.totalMemoryKB * 100
|
||||||
},
|
},
|
||||||
cpu: {
|
cpu: {
|
||||||
load: [0,0,0],
|
load: [0, 0, 0],
|
||||||
usage: cpuUsage,
|
usage: cpuUsage,
|
||||||
count: cpus
|
count: cpus
|
||||||
},
|
},
|
||||||
|
@ -175,24 +175,23 @@
|
|||||||
disabled={loading.save}
|
disabled={loading.save}
|
||||||
>{$t('forms.save')}
|
>{$t('forms.save')}
|
||||||
</button>
|
</button>
|
||||||
{#if !destination.remoteVerified}
|
<button
|
||||||
<button
|
disabled={loading.verify}
|
||||||
disabled={loading.verify}
|
class="btn btn-sm"
|
||||||
class="btn btn-sm"
|
class:loading={loading.verify}
|
||||||
class:loading={loading.verify}
|
on:click|preventDefault|stopPropagation={verifyRemoteDocker}
|
||||||
on:click|preventDefault|stopPropagation={verifyRemoteDocker}
|
>{!destination.remoteVerified
|
||||||
>Verify Remote Docker Engine</button
|
? 'Verify Remote Docker Engine'
|
||||||
>
|
: 'Check Remote Docker Engine'}</button
|
||||||
{:else}
|
>
|
||||||
<button
|
|
||||||
class="btn btn-sm"
|
<button
|
||||||
class:loading={loading.restart}
|
class="btn btn-sm"
|
||||||
class:bg-error={!loading.restart}
|
class:loading={loading.restart}
|
||||||
disabled={loading.restart}
|
class:bg-error={!loading.restart}
|
||||||
on:click|preventDefault={forceRestartProxy}
|
disabled={loading.restart}
|
||||||
>{$t('destination.force_restart_proxy')}</button
|
on:click|preventDefault={forceRestartProxy}>{$t('destination.force_restart_proxy')}</button
|
||||||
>
|
>
|
||||||
{/if}
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center px-10 ">
|
<div class="grid grid-cols-2 items-center px-10 ">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user