diff --git a/engine/internal/retrieval/engine/postgres/logical/logical.go b/engine/internal/retrieval/engine/postgres/logical/logical.go index bd985a87..0d3ea39b 100644 --- a/engine/internal/retrieval/engine/postgres/logical/logical.go +++ b/engine/internal/retrieval/engine/postgres/logical/logical.go @@ -28,7 +28,7 @@ func isAlreadyMounted(mounts []mount.Mount, dir string) bool { dir = strings.Trim(dir, "/") for _, mountPoint := range mounts { - if strings.Trim(mountPoint.Source, "/") == dir { + if strings.Trim(mountPoint.Source, "/") == dir || strings.Trim(mountPoint.Target, "/") == dir { return true } } diff --git a/engine/internal/retrieval/engine/postgres/logical/logical_test.go b/engine/internal/retrieval/engine/postgres/logical/logical_test.go index 0c61cd52..ead6521d 100644 --- a/engine/internal/retrieval/engine/postgres/logical/logical_test.go +++ b/engine/internal/retrieval/engine/postgres/logical/logical_test.go @@ -38,6 +38,16 @@ func TestIsAlreadyMounted(t *testing.T) { dumpLocation: "/var/lib/dblab/new_pool/dump", expectedResult: false, }, + { + source: []mount.Mount{{Source: "/host/path/dump", Target: "/var/lib/dblab/pool/dump"}}, + dumpLocation: "/var/lib/dblab/pool/dump", + expectedResult: true, + }, + { + source: []mount.Mount{{Source: "/host/path/dump", Target: "/var/lib/dblab/pool/dump/"}}, + dumpLocation: "/var/lib/dblab/pool/dump", + expectedResult: true, + }, } for _, tc := range testCases { diff --git a/engine/internal/retrieval/engine/postgres/tools/tools.go b/engine/internal/retrieval/engine/postgres/tools/tools.go index 1fe2cefe..b8ac9ce7 100644 --- a/engine/internal/retrieval/engine/postgres/tools/tools.go +++ b/engine/internal/retrieval/engine/postgres/tools/tools.go @@ -183,6 +183,7 @@ func AddVolumesToHostConfig(ctx context.Context, docker *client.Client, hostConf // GetMountsFromMountPoints creates a list of mounts. func GetMountsFromMountPoints(dataDir string, mountPoints []types.MountPoint) []mount.Mount { mounts := make([]mount.Mount, 0, len(mountPoints)) + seen := make(map[string]struct{}) for _, mountPoint := range mountPoints { // Rewrite mounting to data directory. @@ -192,6 +193,18 @@ func GetMountsFromMountPoints(dataDir string, mountPoints []types.MountPoint) [] mountPoint.Destination = dataDir } + // Deduplicate mounts by normalizing paths and checking both source and target. + normalizedSource := strings.Trim(mountPoint.Source, "/") + normalizedTarget := strings.Trim(mountPoint.Destination, "/") + mountKey := normalizedSource + "|" + normalizedTarget + + if _, ok := seen[mountKey]; ok { + log.Dbg("skipping duplicate mount", mountPoint.Source, "to", mountPoint.Destination) + continue + } + + seen[mountKey] = struct{}{} + mounts = append(mounts, mount.Mount{ Type: mountPoint.Type, Source: mountPoint.Source, diff --git a/engine/internal/retrieval/engine/postgres/tools/tools_test.go b/engine/internal/retrieval/engine/postgres/tools/tools_test.go index 9398470e..3c8aba14 100644 --- a/engine/internal/retrieval/engine/postgres/tools/tools_test.go +++ b/engine/internal/retrieval/engine/postgres/tools/tools_test.go @@ -37,11 +37,13 @@ func TestIfDirectoryEmpty(t *testing.T) { func TestGetMountsFromMountPoints(t *testing.T) { testCases := []struct { + name string dataDir string mountPoints []types.MountPoint expectedPoints []mount.Mount }{ { + name: "simple mount without transformation", dataDir: "/var/lib/dblab/clones/dblab_clone_6000/data", mountPoints: []types.MountPoint{{ Source: "/var/lib/pgsql/data", @@ -56,8 +58,8 @@ func TestGetMountsFromMountPoints(t *testing.T) { }, }}, }, - { + name: "mount with path transformation", dataDir: "/var/lib/dblab/clones/dblab_clone_6000/data", mountPoints: []types.MountPoint{{ Source: "/var/lib/postgresql", @@ -72,10 +74,44 @@ func TestGetMountsFromMountPoints(t *testing.T) { }, }}, }, + { + name: "deduplicate identical mounts", + dataDir: "/var/lib/dblab/data", + mountPoints: []types.MountPoint{ + {Source: "/host/dump", Destination: "/var/lib/dblab/dump"}, + {Source: "/host/dump", Destination: "/var/lib/dblab/dump"}, + }, + expectedPoints: []mount.Mount{{ + Source: "/host/dump", + Target: "/var/lib/dblab/dump", + ReadOnly: true, + BindOptions: &mount.BindOptions{ + Propagation: "", + }, + }}, + }, + { + name: "deduplicate mounts with trailing slashes", + dataDir: "/var/lib/dblab/data", + mountPoints: []types.MountPoint{ + {Source: "/host/dump/", Destination: "/var/lib/dblab/dump"}, + {Source: "/host/dump", Destination: "/var/lib/dblab/dump/"}, + }, + expectedPoints: []mount.Mount{{ + Source: "/host/dump/", + Target: "/var/lib/dblab/dump", + ReadOnly: true, + BindOptions: &mount.BindOptions{ + Propagation: "", + }, + }}, + }, } for _, tc := range testCases { - mounts := GetMountsFromMountPoints(tc.dataDir, tc.mountPoints) - assert.Equal(t, tc.expectedPoints, mounts) + t.Run(tc.name, func(t *testing.T) { + mounts := GetMountsFromMountPoints(tc.dataDir, tc.mountPoints) + assert.Equal(t, tc.expectedPoints, mounts) + }) } } diff --git a/engine/test/_cleanup.sh b/engine/test/_cleanup.sh index 6e9ccca6..de5755a0 100644 --- a/engine/test/_cleanup.sh +++ b/engine/test/_cleanup.sh @@ -3,7 +3,7 @@ set -euxo pipefail DLE_TEST_MOUNT_DIR="/var/lib/test/dblab_mount" DLE_TEST_POOL_NAME="test_dblab_pool" -TMP_DATA_DIR="/tmp/dle_test/logical_generic" +TMP_DATA_DIR="/tmp/dle_test" ZFS_FILE="$(pwd)/zfs_file" # Stop and remove test Docker containers