fix: avoid duplicate volume binds during recreate
This commit is contained in:
@@ -14,6 +14,7 @@ import * as tls from 'node:tls';
|
||||
import { createHash } from 'node:crypto';
|
||||
import type { Environment } from './db';
|
||||
import { getStackEnvVarsAsRecord } from './db';
|
||||
import { getAdditionalVolumeBinds } from './mount-dedupe';
|
||||
import { isSystemContainer } from './scheduler/tasks/update-utils';
|
||||
import { deepDiff } from '../utils/diff.js';
|
||||
|
||||
@@ -1917,20 +1918,7 @@ export async function recreateContainerFromInspect(
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve anonymous volumes from Mounts not in HostConfig.Binds
|
||||
const existingBinds = new Set((hostConfig.Binds || []).map((b: string) => {
|
||||
const parts = b.split(':');
|
||||
return parts.length >= 2 ? parts[1] : parts[0];
|
||||
}));
|
||||
const mounts = inspectData.Mounts || [];
|
||||
const additionalBinds: string[] = [];
|
||||
for (const mount of mounts) {
|
||||
if (mount.Type === 'volume' && mount.Name && mount.Destination) {
|
||||
if (!existingBinds.has(mount.Destination)) {
|
||||
additionalBinds.push(`${mount.Name}:${mount.Destination}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
const additionalBinds = getAdditionalVolumeBinds(hostConfig, inspectData.Mounts || []);
|
||||
if (additionalBinds.length > 0) {
|
||||
createConfig.HostConfig = {
|
||||
...hostConfig,
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { describe, it } from 'node:test';
|
||||
|
||||
import { getAdditionalVolumeBinds } from './mount-dedupe';
|
||||
|
||||
describe('getAdditionalVolumeBinds', () => {
|
||||
it('skips volume mounts when the target already exists in HostConfig.Mounts', () => {
|
||||
const additionalBinds = getAdditionalVolumeBinds(
|
||||
{
|
||||
Binds: ['/volume1/backups:/backup'],
|
||||
Mounts: [{ Target: '/data' }]
|
||||
},
|
||||
[
|
||||
{ Type: 'volume', Name: 'docsight_docsis_data', Destination: '/data' },
|
||||
{ Type: 'bind', Name: 'ignored', Destination: '/backup' }
|
||||
]
|
||||
);
|
||||
|
||||
assert.deepEqual(additionalBinds, []);
|
||||
});
|
||||
|
||||
it('adds volume mounts that are missing from HostConfig', () => {
|
||||
const additionalBinds = getAdditionalVolumeBinds(
|
||||
{
|
||||
Binds: ['/volume1/backups:/backup'],
|
||||
Mounts: []
|
||||
},
|
||||
[{ Type: 'volume', Name: 'docsight_docsis_data', Destination: '/data' }]
|
||||
);
|
||||
|
||||
assert.deepEqual(additionalBinds, ['docsight_docsis_data:/data']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
type HostConfigLike = {
|
||||
Binds?: string[] | null;
|
||||
Mounts?: Array<{ Target?: string | null }> | null;
|
||||
};
|
||||
|
||||
type InspectMountLike = {
|
||||
Type?: string | null;
|
||||
Name?: string | null;
|
||||
Destination?: string | null;
|
||||
};
|
||||
|
||||
/** Build extra bind strings for volume mounts missing from HostConfig. */
|
||||
export function getAdditionalVolumeBinds(
|
||||
hostConfig: HostConfigLike,
|
||||
mounts: InspectMountLike[]
|
||||
): string[] {
|
||||
const existingMountTargets = new Set((hostConfig.Binds || []).map((bind: string) => {
|
||||
const parts = bind.split(':');
|
||||
return parts.length >= 2 ? parts[1] : parts[0];
|
||||
}));
|
||||
|
||||
for (const mount of hostConfig.Mounts || []) {
|
||||
if (mount?.Target) existingMountTargets.add(mount.Target);
|
||||
}
|
||||
|
||||
const additionalBinds: string[] = [];
|
||||
for (const mount of mounts || []) {
|
||||
if (mount.Type === 'volume' && mount.Name && mount.Destination) {
|
||||
if (!existingMountTargets.has(mount.Destination)) {
|
||||
additionalBinds.push(`${mount.Name}:${mount.Destination}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return additionalBinds;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { authorize } from '$lib/server/authorize';
|
||||
import { getAdditionalVolumeBinds } from '$lib/server/mount-dedupe';
|
||||
import {
|
||||
getOwnContainerId,
|
||||
getHostDockerSocket,
|
||||
@@ -166,20 +167,7 @@ function buildCreateConfig(inspectData: any, newImage: string): any {
|
||||
// Otherwise the old container's hostname is inherited, breaking self-identification
|
||||
delete createConfig.Hostname;
|
||||
|
||||
// Preserve anonymous volumes from Mounts not in HostConfig.Binds
|
||||
const existingBinds = new Set((hostConfig.Binds || []).map((b: string) => {
|
||||
const parts = b.split(':');
|
||||
return parts.length >= 2 ? parts[1] : parts[0];
|
||||
}));
|
||||
const mounts = inspectData.Mounts || [];
|
||||
const additionalBinds: string[] = [];
|
||||
for (const mount of mounts) {
|
||||
if (mount.Type === 'volume' && mount.Name && mount.Destination) {
|
||||
if (!existingBinds.has(mount.Destination)) {
|
||||
additionalBinds.push(`${mount.Name}:${mount.Destination}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
const additionalBinds = getAdditionalVolumeBinds(hostConfig, inspectData.Mounts || []);
|
||||
if (additionalBinds.length > 0) {
|
||||
createConfig.HostConfig = {
|
||||
...createConfig.HostConfig,
|
||||
|
||||
Reference in New Issue
Block a user