From c30a7f6911441f92fdf921cb37e58ff2b9fb2f1e Mon Sep 17 00:00:00 2001 From: biersoeckli Date: Fri, 9 Oct 2026 10:37:04 +0000 Subject: [PATCH] fix: implement toDatabaseBackupJobName utility and update backup job naming conventions --- .../mariadb-backup.service.ts | 2 +- .../mongodb-backup.service.ts | 2 +- .../postgres-backup.service.ts | 2 +- src/server/utils/kube-object-name.utils.ts | 11 ++++++++++ .../utils/kube-object-name.utils.unit.spec.ts | 20 +++++++++++++++++++ 5 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/server/services/standalone-services/database-backup-services/mariadb-backup.service.ts b/src/server/services/standalone-services/database-backup-services/mariadb-backup.service.ts index d524ae67..4b040493 100644 --- a/src/server/services/standalone-services/database-backup-services/mariadb-backup.service.ts +++ b/src/server/services/standalone-services/database-backup-services/mariadb-backup.service.ts @@ -17,7 +17,7 @@ class MariaDbBackupService { await namespaceService.createNamespaceIfNotExists(backupNamespace); - const jobName = KubeObjectNameUtils.addRandomSuffix(`backup-mariadb-${app.id}`); + const jobName = KubeObjectNameUtils.toDatabaseBackupJobName('mariadb', app.id); console.log(`Creating MariaDB/MySQL backup job with name: ${jobName}`); const dbCredentials = AppTemplateUtils.getDatabaseModelFromApp(app); diff --git a/src/server/services/standalone-services/database-backup-services/mongodb-backup.service.ts b/src/server/services/standalone-services/database-backup-services/mongodb-backup.service.ts index 91fe16fe..9dfebdb1 100644 --- a/src/server/services/standalone-services/database-backup-services/mongodb-backup.service.ts +++ b/src/server/services/standalone-services/database-backup-services/mongodb-backup.service.ts @@ -16,7 +16,7 @@ class MongoDbBackupService { await namespaceService.createNamespaceIfNotExists(backupNamespace); - const jobName = KubeObjectNameUtils.addRandomSuffix(`backup-mongodb-${app.id}`); + const jobName = KubeObjectNameUtils.toDatabaseBackupJobName('mongodb', app.id); console.log(`Creating MongoDB backup job with name: ${jobName}`); const dbCredentials = AppTemplateUtils.getDatabaseModelFromApp(app); diff --git a/src/server/services/standalone-services/database-backup-services/postgres-backup.service.ts b/src/server/services/standalone-services/database-backup-services/postgres-backup.service.ts index f9de1d6c..e91f30a4 100644 --- a/src/server/services/standalone-services/database-backup-services/postgres-backup.service.ts +++ b/src/server/services/standalone-services/database-backup-services/postgres-backup.service.ts @@ -17,7 +17,7 @@ class PostgresBackupService { await namespaceService.createNamespaceIfNotExists(backupNamespace); - const jobName = KubeObjectNameUtils.addRandomSuffix(`backup-postgres-${app.id}`); + const jobName = KubeObjectNameUtils.toDatabaseBackupJobName('postgres', app.id); console.log(`Creating PostgreSQL backup job with name: ${jobName}`); const dbCredentials = AppTemplateUtils.getDatabaseModelFromApp(app); diff --git a/src/server/utils/kube-object-name.utils.ts b/src/server/utils/kube-object-name.utils.ts index 1ee38e8f..5fbf7cc1 100644 --- a/src/server/utils/kube-object-name.utils.ts +++ b/src/server/utils/kube-object-name.utils.ts @@ -3,6 +3,8 @@ import * as crypto from 'crypto'; export class KubeObjectNameUtils { private static readonly MAX_OBJECT_NAME_LENGTH = 30; // in Kubernetes, the maximum length of an object name is 63 characters + private static readonly MAX_LABEL_VALUE_LENGTH = 63; + private static readonly RANDOM_SUFFIX_LENGTH = 9; // "-" + 8 hexadecimal characters static toSnakeCase(str: string): string { if (!str) { @@ -44,6 +46,15 @@ export class KubeObjectNameUtils { return `build-${appId}`; } + static toDatabaseBackupJobName(database: 'postgres' | 'mongodb' | 'mariadb', appId: string): string { + const prefix = `backup-${database}-`; + const maxAppIdLength = KubeObjectNameUtils.MAX_LABEL_VALUE_LENGTH + - prefix.length + - KubeObjectNameUtils.RANDOM_SUFFIX_LENGTH; + + return KubeObjectNameUtils.addRandomSuffix(`${prefix}${appId.substring(0, maxAppIdLength)}`); + } + static addRandomSuffix(str: string): string { // added suffix is 8 characters long const randomString = crypto.randomBytes(4).toString('hex'); diff --git a/src/server/utils/kube-object-name.utils.unit.spec.ts b/src/server/utils/kube-object-name.utils.unit.spec.ts index 26f033b4..f73582a9 100644 --- a/src/server/utils/kube-object-name.utils.unit.spec.ts +++ b/src/server/utils/kube-object-name.utils.unit.spec.ts @@ -63,6 +63,26 @@ describe('KubeObjectNameUtils', () => { }); }); + describe('toDatabaseBackupJobName', () => { + it.each(['postgres', 'mongodb', 'mariadb'] as const)( + 'should keep %s backup job names within the Kubernetes label and container name limit', + (database) => { + const result = KubeObjectNameUtils.toDatabaseBackupJobName(database, 'a'.repeat(100)); + + expect(result).toMatch(new RegExp(`^backup-${database}-a+-[a-f0-9]{8}$`)); + expect(result.length).toBeLessThanOrEqual(63); + } + ); + + it('should preserve the random suffix when truncating an app ID', () => { + const appId = 'app-ericdaniellwebsitepostgres-b744d884'; + const result = KubeObjectNameUtils.toDatabaseBackupJobName('postgres', appId); + + expect(result).toMatch(new RegExp(`^backup-postgres-${appId.substring(0, 38)}-[a-f0-9]{8}$`)); + expect(result).toHaveLength(63); + }); + }); + describe('addRandomSuffix', () => { it('should add a random suffix to the string', () => { const result = KubeObjectNameUtils.addRandomSuffix('baseString');