From e41f58d63b59676a5043adaeaee2f6ffd96ba264 Mon Sep 17 00:00:00 2001 From: Rostislav Dugin Date: Wed, 24 Dec 2025 22:12:22 +0300 Subject: [PATCH] FIX (mongodb): Add support of minor versions --- .github/workflows/ci-release.yml | 6 +- README.md | 10 +-- backend/.env.development.example | 2 +- backend/docker-compose.yml.example | 8 +-- backend/internal/config/config.go | 2 +- .../features/databases/controller_test.go | 4 +- .../databases/databases/mongodb/model.go | 36 +++++------ .../databases/mongodb/readonly_user_test.go | 32 +++++----- .../tests/mongodb_backup_restore_test.go | 42 ++++++------- backend/internal/util/tools/mongodb.go | 63 ++++++++++--------- 10 files changed, 100 insertions(+), 105 deletions(-) diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index 46d000d..5ffdaf7 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -220,7 +220,7 @@ jobs: TEST_MONGODB_50_PORT=27050 TEST_MONGODB_60_PORT=27060 TEST_MONGODB_70_PORT=27070 - TEST_MONGODB_80_PORT=27080 + TEST_MONGODB_82_PORT=27082 EOF - name: Start test containers @@ -300,8 +300,8 @@ jobs: timeout 120 bash -c 'until docker exec test-mongodb-60 mongosh --eval "db.adminCommand(\"ping\")" -u root -p rootpassword --authenticationDatabase admin 2>/dev/null; do sleep 2; done' echo "Waiting for MongoDB 7.0..." timeout 120 bash -c 'until docker exec test-mongodb-70 mongosh --eval "db.adminCommand(\"ping\")" -u root -p rootpassword --authenticationDatabase admin 2>/dev/null; do sleep 2; done' - echo "Waiting for MongoDB 8.0..." - timeout 120 bash -c 'until docker exec test-mongodb-80 mongosh --eval "db.adminCommand(\"ping\")" -u root -p rootpassword --authenticationDatabase admin 2>/dev/null; do sleep 2; done' + echo "Waiting for MongoDB 8.2..." + timeout 120 bash -c 'until docker exec test-mongodb-82 mongosh --eval "db.adminCommand(\"ping\")" -u root -p rootpassword --authenticationDatabase admin 2>/dev/null; do sleep 2; done' - name: Create data and temp directories run: | diff --git a/README.md b/README.md index 73045f7..1e4b610 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@
Postgresus Logo -

Databases backup tool

-

Postgresus is a free, open source and self-hosted tool to backup databases. Make backups with different storages (S3, Google Drive, FTP, etc.) and notifications about progress (Slack, Discord, Telegram, etc.). PostgreSQL, MySQL, MariaDB and MongoDB are supported.

+

Databases backup tool for PostgreSQL, MySQL and MongoDB

+

Postgresus is a free, open source and self-hosted tool to backup databases. Make backups with different storages (S3, Google Drive, FTP, etc.) and notifications about progress (Slack, Discord, Telegram, etc.)

[![PostgreSQL](https://img.shields.io/badge/PostgreSQL-336791?logo=postgresql&logoColor=white)](https://www.postgresql.org/) @@ -43,9 +43,9 @@ ### 💾 **Supported databases** - **PostgreSQL**: 12, 13, 14, 15, 16, 17 and 18 -- **MySQL**: 5.7, 8.0, 8.4 and 9.x -- **MariaDB**: 10.3, 10.4, 10.5, 10.6, 10.11, 11.0, 11.1, 11.2, 11.3, 11.4, 11.5 and 11.6 -- **MongoDB**: 4.0, 4.2, 4.4, 5.0, 6.0, 7.0 and 8.0 +- **MySQL**: 5.7, 8 and 9 +- **MariaDB**: 10 and 11 +- **MongoDB**: 4, 5, 6, 7 and 8 ### 🔄 **Scheduled backups** diff --git a/backend/.env.development.example b/backend/.env.development.example index 6b1fa64..d3221eb 100644 --- a/backend/.env.development.example +++ b/backend/.env.development.example @@ -67,4 +67,4 @@ TEST_MONGODB_44_PORT=27044 TEST_MONGODB_50_PORT=27050 TEST_MONGODB_60_PORT=27060 TEST_MONGODB_70_PORT=27070 -TEST_MONGODB_80_PORT=27080 \ No newline at end of file +TEST_MONGODB_82_PORT=27082 \ No newline at end of file diff --git a/backend/docker-compose.yml.example b/backend/docker-compose.yml.example index f0c50a6..d392a7e 100644 --- a/backend/docker-compose.yml.example +++ b/backend/docker-compose.yml.example @@ -539,11 +539,11 @@ services: timeout: 5s retries: 10 - test-mongodb-80: - image: mongo:8.0 - container_name: test-mongodb-80 + test-mongodb-82: + image: mongo:8.2.3-noble + container_name: test-mongodb-82 ports: - - "${TEST_MONGODB_80_PORT:-27080}:27017" + - "${TEST_MONGODB_82_PORT:-27082}:27017" environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: rootpassword diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index 74cca24..59654a8 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -77,7 +77,7 @@ type EnvVariables struct { TestMongodb50Port string `env:"TEST_MONGODB_50_PORT"` TestMongodb60Port string `env:"TEST_MONGODB_60_PORT"` TestMongodb70Port string `env:"TEST_MONGODB_70_PORT"` - TestMongodb80Port string `env:"TEST_MONGODB_80_PORT"` + TestMongodb82Port string `env:"TEST_MONGODB_82_PORT"` // oauth GitHubClientID string `env:"GITHUB_CLIENT_ID"` diff --git a/backend/internal/features/databases/controller_test.go b/backend/internal/features/databases/controller_test.go index 18df232..e47f668 100644 --- a/backend/internal/features/databases/controller_test.go +++ b/backend/internal/features/databases/controller_test.go @@ -953,7 +953,7 @@ func Test_DatabaseSensitiveDataLifecycle_AllTypes(t *testing.T) { Name: "Test MongoDB Database", Type: DatabaseTypeMongodb, Mongodb: &mongodb.MongodbDatabase{ - Version: tools.MongodbVersion70, + Version: tools.MongodbVersion7, Host: "localhost", Port: 27017, Username: "root", @@ -971,7 +971,7 @@ func Test_DatabaseSensitiveDataLifecycle_AllTypes(t *testing.T) { Name: "Updated MongoDB Database", Type: DatabaseTypeMongodb, Mongodb: &mongodb.MongodbDatabase{ - Version: tools.MongodbVersion80, + Version: tools.MongodbVersion8, Host: "updated-host", Port: 27018, Username: "updated_user", diff --git a/backend/internal/features/databases/databases/mongodb/model.go b/backend/internal/features/databases/databases/mongodb/model.go index cf29817..b7e475c 100644 --- a/backend/internal/features/databases/databases/mongodb/model.go +++ b/backend/internal/features/databases/databases/mongodb/model.go @@ -381,35 +381,29 @@ func detectMongodbVersion(ctx context.Context, client *mongo.Client) (tools.Mong return "", errors.New("could not parse MongoDB version from buildInfo") } - re := regexp.MustCompile(`^(\d+)\.(\d+)`) + re := regexp.MustCompile(`^(\d+)\.`) matches := re.FindStringSubmatch(versionStr) - if len(matches) < 3 { + if len(matches) < 2 { return "", fmt.Errorf("could not parse MongoDB version: %s", versionStr) } major := matches[1] - minor := matches[2] - versionKey := fmt.Sprintf("%s.%s", major, minor) - switch versionKey { - case "4.0": - return tools.MongodbVersion40, nil - case "4.2": - return tools.MongodbVersion42, nil - case "4.4": - return tools.MongodbVersion44, nil - case "5.0": - return tools.MongodbVersion50, nil - case "6.0": - return tools.MongodbVersion60, nil - case "7.0": - return tools.MongodbVersion70, nil - case "8.0": - return tools.MongodbVersion80, nil + switch major { + case "4": + return tools.MongodbVersion4, nil + case "5": + return tools.MongodbVersion5, nil + case "6": + return tools.MongodbVersion6, nil + case "7": + return tools.MongodbVersion7, nil + case "8": + return tools.MongodbVersion8, nil default: return "", fmt.Errorf( - "unsupported MongoDB version: %s (supported: 4.0, 4.2, 4.4, 5.0, 6.0, 7.0, 8.0)", - versionKey, + "unsupported MongoDB major version: %s (supported: 4.x, 5.x, 6.x, 7.x, 8.x)", + major, ) } } diff --git a/backend/internal/features/databases/databases/mongodb/readonly_user_test.go b/backend/internal/features/databases/databases/mongodb/readonly_user_test.go index f8b4d16..5f1623b 100644 --- a/backend/internal/features/databases/databases/mongodb/readonly_user_test.go +++ b/backend/internal/features/databases/databases/mongodb/readonly_user_test.go @@ -26,13 +26,13 @@ func Test_IsUserReadOnly_AdminUser_ReturnsFalse(t *testing.T) { version tools.MongodbVersion port string }{ - {"MongoDB 4.0", tools.MongodbVersion40, env.TestMongodb40Port}, - {"MongoDB 4.2", tools.MongodbVersion42, env.TestMongodb42Port}, - {"MongoDB 4.4", tools.MongodbVersion44, env.TestMongodb44Port}, - {"MongoDB 5.0", tools.MongodbVersion50, env.TestMongodb50Port}, - {"MongoDB 6.0", tools.MongodbVersion60, env.TestMongodb60Port}, - {"MongoDB 7.0", tools.MongodbVersion70, env.TestMongodb70Port}, - {"MongoDB 8.0", tools.MongodbVersion80, env.TestMongodb80Port}, + {"MongoDB 4.0", tools.MongodbVersion4, env.TestMongodb40Port}, + {"MongoDB 4.2", tools.MongodbVersion4, env.TestMongodb42Port}, + {"MongoDB 4.4", tools.MongodbVersion4, env.TestMongodb44Port}, + {"MongoDB 5.0", tools.MongodbVersion5, env.TestMongodb50Port}, + {"MongoDB 6.0", tools.MongodbVersion6, env.TestMongodb60Port}, + {"MongoDB 7.0", tools.MongodbVersion7, env.TestMongodb70Port}, + {"MongoDB 8.2", tools.MongodbVersion8, env.TestMongodb82Port}, } for _, tc := range cases { @@ -60,13 +60,13 @@ func Test_CreateReadOnlyUser_UserCanReadButNotWrite(t *testing.T) { version tools.MongodbVersion port string }{ - {"MongoDB 4.0", tools.MongodbVersion40, env.TestMongodb40Port}, - {"MongoDB 4.2", tools.MongodbVersion42, env.TestMongodb42Port}, - {"MongoDB 4.4", tools.MongodbVersion44, env.TestMongodb44Port}, - {"MongoDB 5.0", tools.MongodbVersion50, env.TestMongodb50Port}, - {"MongoDB 6.0", tools.MongodbVersion60, env.TestMongodb60Port}, - {"MongoDB 7.0", tools.MongodbVersion70, env.TestMongodb70Port}, - {"MongoDB 8.0", tools.MongodbVersion80, env.TestMongodb80Port}, + {"MongoDB 4.0", tools.MongodbVersion4, env.TestMongodb40Port}, + {"MongoDB 4.2", tools.MongodbVersion4, env.TestMongodb42Port}, + {"MongoDB 4.4", tools.MongodbVersion4, env.TestMongodb44Port}, + {"MongoDB 5.0", tools.MongodbVersion5, env.TestMongodb50Port}, + {"MongoDB 6.0", tools.MongodbVersion6, env.TestMongodb60Port}, + {"MongoDB 7.0", tools.MongodbVersion7, env.TestMongodb70Port}, + {"MongoDB 8.2", tools.MongodbVersion8, env.TestMongodb82Port}, } for _, tc := range cases { @@ -139,7 +139,7 @@ func Test_CreateReadOnlyUser_UserCanReadButNotWrite(t *testing.T) { func Test_ReadOnlyUser_FutureCollections_CanSelect(t *testing.T) { env := config.GetEnv() - container := connectToMongodbContainer(t, env.TestMongodb70Port, tools.MongodbVersion70) + container := connectToMongodbContainer(t, env.TestMongodb70Port, tools.MongodbVersion7) defer container.Client.Disconnect(context.Background()) ctx := context.Background() @@ -170,7 +170,7 @@ func Test_ReadOnlyUser_FutureCollections_CanSelect(t *testing.T) { func Test_ReadOnlyUser_CannotDropOrModifyCollections(t *testing.T) { env := config.GetEnv() - container := connectToMongodbContainer(t, env.TestMongodb70Port, tools.MongodbVersion70) + container := connectToMongodbContainer(t, env.TestMongodb70Port, tools.MongodbVersion7) defer container.Client.Disconnect(context.Background()) ctx := context.Background() diff --git a/backend/internal/features/tests/mongodb_backup_restore_test.go b/backend/internal/features/tests/mongodb_backup_restore_test.go index 320f7bd..3a5b5b9 100644 --- a/backend/internal/features/tests/mongodb_backup_restore_test.go +++ b/backend/internal/features/tests/mongodb_backup_restore_test.go @@ -59,13 +59,13 @@ func Test_BackupAndRestoreMongodb_RestoreIsSuccessful(t *testing.T) { version tools.MongodbVersion port string }{ - {"MongoDB 4.0", tools.MongodbVersion40, env.TestMongodb40Port}, - {"MongoDB 4.2", tools.MongodbVersion42, env.TestMongodb42Port}, - {"MongoDB 4.4", tools.MongodbVersion44, env.TestMongodb44Port}, - {"MongoDB 5.0", tools.MongodbVersion50, env.TestMongodb50Port}, - {"MongoDB 6.0", tools.MongodbVersion60, env.TestMongodb60Port}, - {"MongoDB 7.0", tools.MongodbVersion70, env.TestMongodb70Port}, - {"MongoDB 8.0", tools.MongodbVersion80, env.TestMongodb80Port}, + {"MongoDB 4.0", tools.MongodbVersion4, env.TestMongodb40Port}, + {"MongoDB 4.2", tools.MongodbVersion4, env.TestMongodb42Port}, + {"MongoDB 4.4", tools.MongodbVersion4, env.TestMongodb44Port}, + {"MongoDB 5.0", tools.MongodbVersion5, env.TestMongodb50Port}, + {"MongoDB 6.0", tools.MongodbVersion6, env.TestMongodb60Port}, + {"MongoDB 7.0", tools.MongodbVersion7, env.TestMongodb70Port}, + {"MongoDB 8.2", tools.MongodbVersion8, env.TestMongodb82Port}, } for _, tc := range cases { @@ -83,13 +83,13 @@ func Test_BackupAndRestoreMongodbWithEncryption_RestoreIsSuccessful(t *testing.T version tools.MongodbVersion port string }{ - {"MongoDB 4.0", tools.MongodbVersion40, env.TestMongodb40Port}, - {"MongoDB 4.2", tools.MongodbVersion42, env.TestMongodb42Port}, - {"MongoDB 4.4", tools.MongodbVersion44, env.TestMongodb44Port}, - {"MongoDB 5.0", tools.MongodbVersion50, env.TestMongodb50Port}, - {"MongoDB 6.0", tools.MongodbVersion60, env.TestMongodb60Port}, - {"MongoDB 7.0", tools.MongodbVersion70, env.TestMongodb70Port}, - {"MongoDB 8.0", tools.MongodbVersion80, env.TestMongodb80Port}, + {"MongoDB 4.0", tools.MongodbVersion4, env.TestMongodb40Port}, + {"MongoDB 4.2", tools.MongodbVersion4, env.TestMongodb42Port}, + {"MongoDB 4.4", tools.MongodbVersion4, env.TestMongodb44Port}, + {"MongoDB 5.0", tools.MongodbVersion5, env.TestMongodb50Port}, + {"MongoDB 6.0", tools.MongodbVersion6, env.TestMongodb60Port}, + {"MongoDB 7.0", tools.MongodbVersion7, env.TestMongodb70Port}, + {"MongoDB 8.2", tools.MongodbVersion8, env.TestMongodb82Port}, } for _, tc := range cases { @@ -107,13 +107,13 @@ func Test_BackupAndRestoreMongodb_WithReadOnlyUser_RestoreIsSuccessful(t *testin version tools.MongodbVersion port string }{ - {"MongoDB 4.0", tools.MongodbVersion40, env.TestMongodb40Port}, - {"MongoDB 4.2", tools.MongodbVersion42, env.TestMongodb42Port}, - {"MongoDB 4.4", tools.MongodbVersion44, env.TestMongodb44Port}, - {"MongoDB 5.0", tools.MongodbVersion50, env.TestMongodb50Port}, - {"MongoDB 6.0", tools.MongodbVersion60, env.TestMongodb60Port}, - {"MongoDB 7.0", tools.MongodbVersion70, env.TestMongodb70Port}, - {"MongoDB 8.0", tools.MongodbVersion80, env.TestMongodb80Port}, + {"MongoDB 4.0", tools.MongodbVersion4, env.TestMongodb40Port}, + {"MongoDB 4.2", tools.MongodbVersion4, env.TestMongodb42Port}, + {"MongoDB 4.4", tools.MongodbVersion4, env.TestMongodb44Port}, + {"MongoDB 5.0", tools.MongodbVersion5, env.TestMongodb50Port}, + {"MongoDB 6.0", tools.MongodbVersion6, env.TestMongodb60Port}, + {"MongoDB 7.0", tools.MongodbVersion7, env.TestMongodb70Port}, + {"MongoDB 8.2", tools.MongodbVersion8, env.TestMongodb82Port}, } for _, tc := range cases { diff --git a/backend/internal/util/tools/mongodb.go b/backend/internal/util/tools/mongodb.go index 66cc668..7ab1939 100644 --- a/backend/internal/util/tools/mongodb.go +++ b/backend/internal/util/tools/mongodb.go @@ -5,6 +5,7 @@ import ( "log/slog" "os" "path/filepath" + "regexp" "runtime" env_utils "postgresus-backend/internal/util/env" @@ -13,13 +14,11 @@ import ( type MongodbVersion string const ( - MongodbVersion40 MongodbVersion = "4.0" - MongodbVersion42 MongodbVersion = "4.2" - MongodbVersion44 MongodbVersion = "4.4" - MongodbVersion50 MongodbVersion = "5.0" - MongodbVersion60 MongodbVersion = "6.0" - MongodbVersion70 MongodbVersion = "7.0" - MongodbVersion80 MongodbVersion = "8.0" + MongodbVersion4 MongodbVersion = "4" + MongodbVersion5 MongodbVersion = "5" + MongodbVersion6 MongodbVersion = "6" + MongodbVersion7 MongodbVersion = "7" + MongodbVersion8 MongodbVersion = "8" ) type MongodbExecutable string @@ -123,36 +122,38 @@ func IsMongodbBackupVersionHigherThanRestoreVersion( backupVersion, restoreVersion MongodbVersion, ) bool { versionOrder := map[MongodbVersion]int{ - MongodbVersion40: 1, - MongodbVersion42: 2, - MongodbVersion44: 3, - MongodbVersion50: 4, - MongodbVersion60: 5, - MongodbVersion70: 6, - MongodbVersion80: 7, + MongodbVersion4: 4, + MongodbVersion5: 5, + MongodbVersion6: 6, + MongodbVersion7: 7, + MongodbVersion8: 8, } return versionOrder[backupVersion] > versionOrder[restoreVersion] } -// GetMongodbVersionEnum converts a version string to MongodbVersion enum +// GetMongodbVersionEnum converts a version string to MongodbVersion enum. +// Accepts full version strings (e.g., "8.2", "5.0.1") and extracts the major version. func GetMongodbVersionEnum(version string) MongodbVersion { - switch version { - case "4.0": - return MongodbVersion40 - case "4.2": - return MongodbVersion42 - case "4.4": - return MongodbVersion44 - case "5.0": - return MongodbVersion50 - case "6.0": - return MongodbVersion60 - case "7.0": - return MongodbVersion70 - case "8.0": - return MongodbVersion80 + re := regexp.MustCompile(`^(\d+)`) + matches := re.FindStringSubmatch(version) + if len(matches) < 2 { + panic(fmt.Sprintf("invalid mongodb version format: %s", version)) + } + + major := matches[1] + switch major { + case "4": + return MongodbVersion4 + case "5": + return MongodbVersion5 + case "6": + return MongodbVersion6 + case "7": + return MongodbVersion7 + case "8": + return MongodbVersion8 default: - panic(fmt.Sprintf("invalid mongodb version: %s", version)) + panic(fmt.Sprintf("unsupported mongodb major version: %s", major)) } }