mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
fix(ql3): require read-only legacy target mount
This commit is contained in:
@@ -361,6 +361,7 @@ function mappedMount(
|
|||||||
hostPath: string,
|
hostPath: string,
|
||||||
targetPath: string,
|
targetPath: string,
|
||||||
label: string,
|
label: string,
|
||||||
|
expectedReadWrite = true,
|
||||||
): DockerMount {
|
): DockerMount {
|
||||||
const matches = mounts.filter((mount) => {
|
const matches = mounts.filter((mount) => {
|
||||||
const relative = path.relative(mount.source, hostPath);
|
const relative = path.relative(mount.source, hostPath);
|
||||||
@@ -370,8 +371,13 @@ function mappedMount(
|
|||||||
path.join(mount.destination, relative) === targetPath
|
path.join(mount.destination, relative) === targetPath
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
if (matches.length !== 1 || matches[0]?.readWrite !== true) {
|
if (
|
||||||
configurationError(`${label} must have one read-write bind mapping`);
|
matches.length !== 1 ||
|
||||||
|
matches[0]?.readWrite !== expectedReadWrite
|
||||||
|
) {
|
||||||
|
configurationError(
|
||||||
|
`${label} must have one ${expectedReadWrite ? 'read-write' : 'read-only'} bind mapping`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return matches[0]!;
|
return matches[0]!;
|
||||||
}
|
}
|
||||||
@@ -597,6 +603,7 @@ export function parseTargetContainerEvidence(
|
|||||||
command.request.legacySourcePath,
|
command.request.legacySourcePath,
|
||||||
application.targetLegacySourcePath,
|
application.targetLegacySourcePath,
|
||||||
'target legacy source',
|
'target legacy source',
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
const databaseMount = mappedMount(
|
const databaseMount = mappedMount(
|
||||||
mounts,
|
mounts,
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ function stoppedLegacyInspection(state, running = false) {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function targetInspection(state) {
|
function targetInspection(state, options = {}) {
|
||||||
return JSON.stringify([
|
return JSON.stringify([
|
||||||
{
|
{
|
||||||
Id: state.targetContainerId,
|
Id: state.targetContainerId,
|
||||||
@@ -115,10 +115,27 @@ function targetInspection(state) {
|
|||||||
Mounts: [
|
Mounts: [
|
||||||
{
|
{
|
||||||
Type: 'bind',
|
Type: 'bind',
|
||||||
Source: state.managementRoot,
|
Source: state.deploymentRoot,
|
||||||
Destination: '/host',
|
Destination: targetPath(state, state.deploymentRoot),
|
||||||
RW: true,
|
RW: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Type: 'bind',
|
||||||
|
Source: state.legacySourcePath,
|
||||||
|
Destination: targetPath(state, state.legacySourcePath),
|
||||||
|
RW: options.writableLegacySource === true,
|
||||||
|
},
|
||||||
|
...[
|
||||||
|
state.targetDatabasePath,
|
||||||
|
state.recoveryPath,
|
||||||
|
state.manifestPath,
|
||||||
|
state.activationPath,
|
||||||
|
].map((filePath) => ({
|
||||||
|
Type: 'bind',
|
||||||
|
Source: filePath,
|
||||||
|
Destination: targetPath(state, filePath),
|
||||||
|
RW: true,
|
||||||
|
})),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
@@ -472,7 +489,7 @@ function harness(state, options = {}) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (args[0] === 'container' && args[1] === 'inspect') {
|
if (args[0] === 'container' && args[1] === 'inspect') {
|
||||||
return targetInspection(state);
|
return targetInspection(state, options);
|
||||||
}
|
}
|
||||||
if (args[0] === 'container' && args[1] === 'start') {
|
if (args[0] === 'container' && args[1] === 'start') {
|
||||||
if (args[2] === state.legacyContainerId) {
|
if (args[2] === state.legacyContainerId) {
|
||||||
@@ -574,6 +591,27 @@ test('starts an exact target once and replays the active commitment without Dock
|
|||||||
assert.equal(replay.recordDigest, active.recordDigest);
|
assert.equal(replay.recordDigest, active.recordDigest);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('requires the target container to keep the Legacy source read-only', async (t) => {
|
||||||
|
const state = fixture(t);
|
||||||
|
const controller = harness(state, { writableLegacySource: true });
|
||||||
|
const result = await runLocalDeploymentDockerTarget(
|
||||||
|
command(state),
|
||||||
|
controller,
|
||||||
|
);
|
||||||
|
assert.equal(result.state, 'manual_required');
|
||||||
|
assert.equal(
|
||||||
|
controller.calls.filter((args) => args[1] === 'start').length,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
const request = JSON.parse(
|
||||||
|
fs.readFileSync(
|
||||||
|
path.join(state.journal, '0003-target-start-decision.json'),
|
||||||
|
'utf8',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
assert.equal(request.evidence.reason, 'target_preflight_unproved');
|
||||||
|
});
|
||||||
|
|
||||||
test('starts an offline Trial Kit image only when its local reference and content ID both match', async (t) => {
|
test('starts an offline Trial Kit image only when its local reference and content ID both match', async (t) => {
|
||||||
const state = fixture(t);
|
const state = fixture(t);
|
||||||
state.targetImageAuthority = 'local-image-id';
|
state.targetImageAuthority = 'local-image-id';
|
||||||
|
|||||||
Reference in New Issue
Block a user