Skip to content

Commit

Permalink
feat: Databases on ARM
Browse files Browse the repository at this point in the history
  • Loading branch information
andrasbacsai committed Aug 12, 2022
1 parent 5d5a478 commit 8d3ca92
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 62 deletions.
5 changes: 3 additions & 2 deletions apps/api/src/jobs/checkProxies.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { parentPort } from 'node:worker_threads';
import { prisma, startTraefikTCPProxy, generateDatabaseConfiguration, startTraefikProxy, executeDockerCmd } from '../lib/common';
import { prisma, startTraefikTCPProxy, generateDatabaseConfiguration, startTraefikProxy, executeDockerCmd, listSettings } from '../lib/common';
import { checkContainer } from '../lib/docker';

(async () => {
if (parentPort) {
try {
const { arch } = await listSettings();
// Coolify Proxy local
const engine = '/var/run/docker.sock';
const localDocker = await prisma.destinationDocker.findFirst({
Expand All @@ -30,7 +31,7 @@ import { checkContainer } from '../lib/docker';
for (const database of databasesWithPublicPort) {
const { destinationDockerId, destinationDocker, publicPort, id } = database;
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
const { privatePort } = generateDatabaseConfiguration(database);
const { privatePort } = generateDatabaseConfiguration(database, arch);
// Remove HAProxy
const found = await checkContainer({
dockerId: localDocker.id, container: `haproxy-for-${publicPort}`
Expand Down
107 changes: 89 additions & 18 deletions apps/api/src/lib/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { checkContainer, removeContainer } from './docker';
import { day } from './dayjs';
import * as serviceFields from './serviceFields'

export const version = '3.2.3';
export const version = '3.3.0';
export const isDev = process.env.NODE_ENV === 'development';

const algorithm = 'aes-256-ctr';
Expand Down Expand Up @@ -462,26 +462,46 @@ export const supportedDatabaseTypesAndVersions = [
baseImage: 'bitnami/mongodb',
versions: ['5.0', '4.4', '4.2']
},
{ name: 'mysql', fancyName: 'MySQL', baseImage: 'bitnami/mysql', versions: ['8.0', '5.7'] },
{
name: 'mysql',
fancyName: 'MySQL',
baseImage: 'bitnami/mysql',
baseImageARM: 'mysql',
versions: ['8.0', '5.7'],
versionsARM: ['8.0', '5.7']
},
{
name: 'mariadb',
fancyName: 'MariaDB',
baseImage: 'bitnami/mariadb',
versions: ['10.8', '10.7', '10.6', '10.5', '10.4', '10.3', '10.2']
baseImageARM: 'mariadb',
versions: ['10.8', '10.7', '10.6', '10.5', '10.4', '10.3', '10.2'],
versionsARM: ['10.8', '10.7', '10.6', '10.5', '10.4', '10.3', '10.2']
},
{
name: 'postgresql',
fancyName: 'PostgreSQL',
baseImage: 'bitnami/postgresql',
versions: ['14.4.0', '13.6.0', '12.10.0', '11.15.0', '10.20.0']
baseImageARM: 'postgres',
versions: ['14.5.0', '13.8.0', '12.12.0', '11.17.0', '10.22.0'],
versionsARM: ['14.5', '13.8', '12.12', '11.17', '10.22']
},
{
name: 'redis',
fancyName: 'Redis',
baseImage: 'bitnami/redis',
versions: ['7.0', '6.2', '6.0', '5.0']
baseImageARM: 'redis',
versions: ['7.0', '6.2', '6.0', '5.0'],
versionsARM: ['7.0', '6.2', '6.0', '5.0']
},
{ name: 'couchdb', fancyName: 'CouchDB', baseImage: 'bitnami/couchdb', versions: ['3.2.2'] }
{
name: 'couchdb',
fancyName: 'CouchDB',
baseImage: 'bitnami/couchdb',
baseImageARM: 'couchdb',
versions: ['3.2.2', '3.1.2', '2.3.1'],
versionsARM: ['3.2.2', '3.1.2', '2.3.1']
}
];

export async function getFreeSSHLocalPort(id: string): Promise<number> {
Expand Down Expand Up @@ -674,10 +694,11 @@ export function generatePassword(length = 24, symbols = false): string {
});
}

export function generateDatabaseConfiguration(database: any):
export function generateDatabaseConfiguration(database: any, arch: string):
| {
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
Expand All @@ -691,6 +712,7 @@ export function generateDatabaseConfiguration(database: any):
| {
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
Expand All @@ -701,6 +723,7 @@ export function generateDatabaseConfiguration(database: any):
| {
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
Expand All @@ -714,6 +737,7 @@ export function generateDatabaseConfiguration(database: any):
| {
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
Expand All @@ -726,6 +750,19 @@ export function generateDatabaseConfiguration(database: any):
| {
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
POSTGRES_USER: string;
POSTGRES_PASSWORD: string;
POSTGRES_DB: string;
};
}
| {
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
Expand All @@ -736,6 +773,7 @@ export function generateDatabaseConfiguration(database: any):
| {
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
Expand All @@ -754,9 +792,9 @@ export function generateDatabaseConfiguration(database: any):
type,
settings: { appendOnly }
} = database;
const baseImage = getDatabaseImage(type);
const baseImage = getDatabaseImage(type, arch);
if (type === 'mysql') {
return {
const configuration = {
privatePort: 3306,
environmentVariables: {
MYSQL_USER: dbUser,
Expand All @@ -768,9 +806,13 @@ export function generateDatabaseConfiguration(database: any):
image: `${baseImage}:${version}`,
volume: `${id}-${type}-data:/bitnami/mysql/data`,
ulimits: {}
};
}
if (!isARM(arch)) {
configuration.volume = `${id}-${type}-data:/var/lib/mysql`;
}
return configuration
} else if (type === 'mariadb') {
return {
const configuration = {
privatePort: 3306,
environmentVariables: {
MARIADB_ROOT_USER: rootUser,
Expand All @@ -783,6 +825,10 @@ export function generateDatabaseConfiguration(database: any):
volume: `${id}-${type}-data:/bitnami/mariadb`,
ulimits: {}
};
if (!isARM(arch)) {
configuration.volume = `${id}-${type}-data:/var/lib/mysql`;
}
return configuration
} else if (type === 'mongodb') {
return {
privatePort: 27017,
Expand All @@ -795,7 +841,7 @@ export function generateDatabaseConfiguration(database: any):
ulimits: {}
};
} else if (type === 'postgresql') {
return {
const configuration = {
privatePort: 5432,
environmentVariables: {
POSTGRESQL_POSTGRES_PASSWORD: rootUserPassword,
Expand All @@ -806,10 +852,15 @@ export function generateDatabaseConfiguration(database: any):
image: `${baseImage}:${version}`,
volume: `${id}-${type}-data:/bitnami/postgresql`,
ulimits: {}
};
}
if (!isARM(arch)) {
configuration.volume = `${id}-${type}-data:/var/lib/postgresql`;
}
return configuration
} else if (type === 'redis') {
return {
const configuration = {
privatePort: 6379,
command: undefined,
environmentVariables: {
REDIS_PASSWORD: dbUserPassword,
REDIS_AOF_ENABLED: appendOnly ? 'yes' : 'no'
Expand All @@ -818,8 +869,13 @@ export function generateDatabaseConfiguration(database: any):
volume: `${id}-${type}-data:/bitnami/redis/data`,
ulimits: {}
};
if (!isARM(arch)) {
configuration.volume = `${id}-${type}-data:/data`;
configuration.command = `/usr/local/bin/redis-server --appendonly ${appendOnly ? 'yes' : 'no'} --requirepass ${dbUserPassword}`;
}
return configuration
} else if (type === 'couchdb') {
return {
const configuration = {
privatePort: 5984,
environmentVariables: {
COUCHDB_PASSWORD: dbUserPassword,
Expand All @@ -829,20 +885,35 @@ export function generateDatabaseConfiguration(database: any):
volume: `${id}-${type}-data:/bitnami/couchdb`,
ulimits: {}
};
if (!isARM(arch)) {
configuration.volume = `${id}-${type}-data:/opt/couchdb/data`;
}
return configuration
}
}

export function getDatabaseImage(type: string): string {
export function isARM(arch) {
if (arch === 'arm' || arch === 'arm64') {
return true
}
return false
}
export function getDatabaseImage(type: string, arch: string): string {
const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type);
if (found) {
if (!isARM(arch)) {
return found.baseImageARM || found.baseImage
}
return found.baseImage;
}
return '';
}

export function getDatabaseVersions(type: string): string[] {
export function getDatabaseVersions(type: string, arch: string): string[] {
const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type);
if (found) {
if (!isARM(arch)) {
return found.versionsARM || found.versions
}
return found.versions;
}
return [];
Expand Down
22 changes: 15 additions & 7 deletions apps/api/src/routes/api/v1/databases/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { FastifyRequest } from 'fastify';
import { FastifyReply } from 'fastify';
import yaml from 'js-yaml';
import fs from 'fs/promises';
import { ComposeFile, createDirectories, decrypt, encrypt, errorHandler, executeDockerCmd, generateDatabaseConfiguration, generatePassword, getContainerUsage, getDatabaseImage, getDatabaseVersions, getFreePublicPort, listSettings, makeLabelForStandaloneDatabase, prisma, startTraefikTCPProxy, stopDatabaseContainer, stopTcpHttpProxy, supportedDatabaseTypesAndVersions, uniqueName, updatePasswordInDb } from '../../../../lib/common';
import { ComposeFile, createDirectories, decrypt, encrypt, errorHandler, executeDockerCmd, generateDatabaseConfiguration, generatePassword, getContainerUsage, getDatabaseImage, getDatabaseVersions, getFreePublicPort, isARM, listSettings, makeLabelForStandaloneDatabase, prisma, startTraefikTCPProxy, stopDatabaseContainer, stopTcpHttpProxy, supportedDatabaseTypesAndVersions, uniqueName, updatePasswordInDb } from '../../../../lib/common';
import { checkContainer } from '../../../../lib/docker';
import { day } from '../../../../lib/dayjs';

Expand Down Expand Up @@ -93,14 +93,15 @@ export async function getDatabase(request: FastifyRequest<OnlyId>) {
if (!database) {
throw { status: 404, message: 'Database not found.' }
}
const { arch } = await listSettings();
if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword);
if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword);
const configuration = generateDatabaseConfiguration(database);
const configuration = generateDatabaseConfiguration(database, arch);
const settings = await listSettings();
return {
privatePort: configuration?.privatePort,
database,
versions: await getDatabaseVersions(database.type),
versions: await getDatabaseVersions(database.type, arch),
settings
};
} catch ({ status, message }) {
Expand Down Expand Up @@ -137,8 +138,10 @@ export async function getVersions(request: FastifyRequest<OnlyId>) {
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { destinationDocker: true, settings: true }
});
const { arch } = await listSettings();
const versions = getDatabaseVersions(type, arch);
return {
versions: supportedDatabaseTypesAndVersions.find((name) => name.name === type).versions
versions
}
} catch ({ status, message }) {
return errorHandler({ status, message })
Expand Down Expand Up @@ -219,6 +222,7 @@ export async function startDatabase(request: FastifyRequest<OnlyId>) {
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { destinationDocker: true, settings: true }
});
const { arch } = await listSettings();
if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword);
if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword);
const {
Expand All @@ -228,8 +232,8 @@ export async function startDatabase(request: FastifyRequest<OnlyId>) {
publicPort,
settings: { isPublic }
} = database;
const { privatePort, environmentVariables, image, volume, ulimits } =
generateDatabaseConfiguration(database);
const { privatePort, command, environmentVariables, image, volume, ulimits } =
generateDatabaseConfiguration(database, arch);

const network = destinationDockerId && destinationDocker.network;
const volumeName = volume.split(':')[0];
Expand All @@ -243,6 +247,7 @@ export async function startDatabase(request: FastifyRequest<OnlyId>) {
[id]: {
container_name: id,
image,
command,
networks: [network],
environment: environmentVariables,
volumes: [volume],
Expand Down Expand Up @@ -270,6 +275,7 @@ export async function startDatabase(request: FastifyRequest<OnlyId>) {
}
}
};

const composeFileDestination = `${workdir}/docker-compose.yaml`;
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
try {
Expand All @@ -282,6 +288,7 @@ export async function startDatabase(request: FastifyRequest<OnlyId>) {
if (isPublic) await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort);
return {};
} catch (error) {
console.log(error)
throw {
error
};
Expand Down Expand Up @@ -440,11 +447,12 @@ export async function saveDatabaseSettings(request: FastifyRequest<SaveDatabaseS
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { destinationDocker: true, settings: true }
});
const { arch } = await listSettings();
if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword);
if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword);

const { destinationDockerId, destinationDocker, publicPort: oldPublicPort } = database;
const { privatePort } = generateDatabaseConfiguration(database);
const { privatePort } = generateDatabaseConfiguration(database, arch);

if (destinationDockerId) {
if (isPublic) {
Expand Down
29 changes: 0 additions & 29 deletions apps/ui/src/lib/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,35 +232,6 @@ export function changeQueryParams(buildId: string) {
return history.pushState(null, null, '?' + queryParams.toString());
}

export const supportedDatabaseTypesAndVersions = [
{
name: 'mongodb',
fancyName: 'MongoDB',
baseImage: 'bitnami/mongodb',
versions: ['5.0', '4.4', '4.2']
},
{ name: 'mysql', fancyName: 'MySQL', baseImage: 'bitnami/mysql', versions: ['8.0', '5.7'] },
{
name: 'mariadb',
fancyName: 'MariaDB',
baseImage: 'bitnami/mariadb',
versions: ['10.7', '10.6', '10.5', '10.4', '10.3', '10.2']
},
{
name: 'postgresql',
fancyName: 'PostgreSQL',
baseImage: 'bitnami/postgresql',
versions: ['14.2.0', '13.6.0', '12.10.0 ', '11.15.0', '10.20.0']
},
{
name: 'redis',
fancyName: 'Redis',
baseImage: 'bitnami/redis',
versions: ['6.2', '6.0', '5.0']
},
{ name: 'couchdb', fancyName: 'CouchDB', baseImage: 'bitnami/couchdb', versions: ['3.2.1'] }
];

export const getServiceMainPort = (service: string) => {
const serviceType = supportedServiceTypesAndVersions.find((s) => s.name === service);
if (serviceType) {
Expand Down
Loading

0 comments on commit 8d3ca92

Please sign in to comment.