From 9f90f5aded559f0b431cf289063e962f6714a1f5 Mon Sep 17 00:00:00 2001 From: Piotr Hajdas Date: Tue, 24 Dec 2024 20:59:30 +0100 Subject: [PATCH] feat(do): added DigitalOcean Database Support --- package-lock.json | 85 ----------------------- src/config/digitalocean/database-types.ts | 43 ++++++++++++ src/parsers/digitalocean.ts | 19 +++-- src/utils/getDigitalOceanDatabaseType.ts | 24 +++++++ 4 files changed, 82 insertions(+), 89 deletions(-) create mode 100644 src/config/digitalocean/database-types.ts create mode 100644 src/utils/getDigitalOceanDatabaseType.ts diff --git a/package-lock.json b/package-lock.json index 009ee1a..52ea44e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1082,23 +1082,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.0.tgz", - "integrity": "sha512-PNGcHop0jkK2WVYGotk/hxj+UFLhXtGPiGtiaWgVBVP1jhMoMCHlTyJA+hEj4rszoSdLTK3fN4oOatrL0Cp+Xw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.18.0", - "@typescript-eslint/visitor-keys": "8.18.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/type-utils": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.1.tgz", @@ -1190,45 +1173,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.0.tgz", - "integrity": "sha512-FNYxgyTCAnFwTrzpBGq+zrnoTO4x0c1CKYY5MuUTzpScqmY5fmsh2o3+57lqdI3NZucBDCzDgdEbIaNfAjAHQA==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.0.tgz", - "integrity": "sha512-rqQgFRu6yPkauz+ms3nQpohwejS8bvgbPyIDq13cgEDbkXt4LH4OkDMT0/fN1RUtzG8e8AKJyDBoocuQh8qNeg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.18.0", - "@typescript-eslint/visitor-keys": "8.18.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" - } - }, "node_modules/@typescript-eslint/utils": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.1.tgz", @@ -1337,35 +1281,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.0.tgz", - "integrity": "sha512-pCh/qEA8Lb1wVIqNvBke8UaRjJ6wrAWkJO5yyIbs8Yx6TNGYyfNjOo61tLv+WwLvoLPp4BQ8B7AHKijl8NGUfw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.18.0", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", diff --git a/src/config/digitalocean/database-types.ts b/src/config/digitalocean/database-types.ts new file mode 100644 index 0000000..e6589af --- /dev/null +++ b/src/config/digitalocean/database-types.ts @@ -0,0 +1,43 @@ +interface DatabaseConfig { + engine: string; + versions: string[]; + description: string; +} + +interface DigitalOceanDatabaseConfig { + databases: { + [key: string]: DatabaseConfig; + }; +} + +export const digitalOceanDatabaseConfig: DigitalOceanDatabaseConfig = { + databases: { + 'docker.io/library/mysql': { + engine: 'MYSQL', + versions: ['8'], + description: 'MySQL database service - requires managed database service due to TCP protocol' + }, + 'docker.io/library/mariadb': { + engine: 'MYSQL', + versions: ['8'], + description: 'MariaDB database service - maps to MySQL managed database due to compatibility' + }, + 'docker.io/library/postgres': { + engine: 'PG', + versions: ['13', '14', '15'], + description: 'PostgreSQL database service - requires managed database service due to TCP protocol' + }, + 'docker.io/library/redis': { + engine: 'REDIS', + versions: ['6', '7'], + description: 'Redis database service - requires managed database service due to TCP protocol' + }, + 'docker.io/library/mongodb': { + engine: 'MONGODB', + versions: ['6.0', '7.0'], + description: 'MongoDB database service - requires managed database service due to TCP protocol' + } + } +}; + +export type { DatabaseConfig, DigitalOceanDatabaseConfig }; \ No newline at end of file diff --git a/src/parsers/digitalocean.ts b/src/parsers/digitalocean.ts index 260af1c..2d68d64 100644 --- a/src/parsers/digitalocean.ts +++ b/src/parsers/digitalocean.ts @@ -5,6 +5,7 @@ import { parseCommand } from '../utils/parseCommand'; import { digitalOceanParserServiceName } from '../utils/digitalOceanParserServiceName'; import { parseEnvironmentVariables } from '../utils/parseEnvironmentVariables'; import { constructImageString } from '../utils/constructImageString'; +import { getDigitalOceanDatabaseType } from '../utils/getDigitalOceanDatabaseType'; const defaultParserConfig: DefaultParserConfig = { templateFormat: TemplateFormat.yaml, @@ -13,9 +14,6 @@ const defaultParserConfig: DefaultParserConfig = { subscriptionName: 'basic-xxs' }; -/** - * Gets the registry type for DigitalOcean format based on Docker image info - */ function getRegistryType(dockerImageInfo: DockerImageInfo): string { switch (dockerImageInfo.registry_type) { case 'GHCR': @@ -29,9 +27,22 @@ function getRegistryType(dockerImageInfo: DockerImageInfo): string { class DigitalOceanParser extends BaseParser { parse(config: ApplicationConfig, templateFormat: TemplateFormat = defaultParserConfig.templateFormat): any { const services: Array = []; + const databases: Array = []; let isFirstService = true; for (const [serviceName, serviceConfig] of Object.entries(config.services)) { + const databaseInfo = getDigitalOceanDatabaseType(serviceConfig.image); + + if (databaseInfo) { + databases.push({ + name: serviceName, + engine: databaseInfo.engine, + version: databaseInfo.version, + production: false + }); + continue; + } + const ports = new Set(); if (serviceConfig.ports) { @@ -47,7 +58,6 @@ class DigitalOceanParser extends BaseParser { }); } - // Use the image info directly since it's already a DockerImageInfo object const dockerImageInfo = serviceConfig.image; const imageString = constructImageString(dockerImageInfo); const [repository, tag] = imageString.split(':'); @@ -83,6 +93,7 @@ class DigitalOceanParser extends BaseParser { spec: { name: 'deploystack', region: defaultParserConfig.region, + ...(databases.length > 0 && { databases }), services: services.map(service => ({ name: service.name, image: service.image, diff --git a/src/utils/getDigitalOceanDatabaseType.ts b/src/utils/getDigitalOceanDatabaseType.ts new file mode 100644 index 0000000..b385144 --- /dev/null +++ b/src/utils/getDigitalOceanDatabaseType.ts @@ -0,0 +1,24 @@ +import { DockerImageInfo } from '../parsers/base-parser'; +import { getImageUrl } from './getImageUrl'; +import { constructImageString } from './constructImageString'; +import { digitalOceanDatabaseConfig } from '../config/digitalocean/database-types'; + +export interface DatabaseInfo { + engine: string; + version: string; +} + +export function getDigitalOceanDatabaseType(image: DockerImageInfo): DatabaseInfo | null { + const normalizedImage = getImageUrl(constructImageString(image)); + + for (const [configImage, dbConfig] of Object.entries(digitalOceanDatabaseConfig.databases)) { + if (normalizedImage.includes(configImage)) { + return { + engine: dbConfig.engine, + version: dbConfig.versions[0] + }; + } + } + + return null; +} \ No newline at end of file