Veröffentlichte Quellen
Quellen als ZIP herunterladen
CH-J Tools
Durchsuchen Sie Verzeichnisse und Dateien einer bestimmten Anwendungsausgabe.
CH-J / Server Manager
406 Dateien
core-20261024T135205Z-7c487d719de2
Hinzugefügt: · Erstellt:
1
"use strict";3
// chj.disks against ONE isolated Linux test VM over real SSH (key login, sudo with password), through the real4
// DiskService/PrivilegedExecutor/remoteDisks.sh. Read-only checks always run. Destructive checks run only with5
// CHJ_DISKS_WRITE=1 and only on loop devices backed by /var/tmp/chj-disks-test/chjtest-<run>.img created by this test6
// (verified before every change); the system disk is never a target. Proxmox hosts get the read-only part only.7
// Environment as test/linuxVmIntegration.test.js: CHJ_VM_HOST, CHJ_VM_FINGERPRINT, CHJ_VM_KEY, CHJ_VM_USER (ai),8
// CHJ_VM_SUDO_PASSWORD or CHJ_VM_SUDO_PASSWORD_FILE + CHJ_VM_SUDO_PASSWORD_LINE (read at runtime, never printed).9
const test = require("node:test");10
const assert = require("node:assert/strict");11
const crypto = require("node:crypto");12
const fs = require("node:fs");13
const path = require("node:path");14
const { SessionManager } = require("../src/main/sessions/sessionManager");15
const { PrivilegedExecutor } = require("../src/main/sessions/privilegedExec");16
const { DiskService } = require("../src/main/disks/diskService");18
const env = process.env;19
const enabled = Boolean(env.CHJ_VM_HOST && env.CHJ_VM_FINGERPRINT && env.CHJ_VM_KEY && (env.CHJ_VM_SUDO_PASSWORD || env.CHJ_VM_SUDO_PASSWORD_FILE));20
const skip = enabled ? false : "Set CHJ_VM_* to run against an isolated Linux test VM.";21
const sudoPassword = !enabled ? "" : env.CHJ_VM_SUDO_PASSWORD22
|| fs.readFileSync(env.CHJ_VM_SUDO_PASSWORD_FILE, "utf8").split("\n")[Number(env.CHJ_VM_SUDO_PASSWORD_LINE || 1) - 1].split("=").slice(1).join("=").trim();23
const FIXTURE = fs.readFileSync(path.join(__dirname, "fixtures", "disks", "loopFixture.sh"), "utf8");24
const MiB = 1048576;25
const RUN = crypto.randomBytes(4).toString("hex");26
const TEST_IMAGE = /^\/var\/tmp\/chj-disks-test\/chjtest-[a-z0-9]+\.img$/;28
test("chj.disks on an isolated Linux test VM", { skip, timeout: 1800000 }, async (t) => {29
const hostKeys = new Map();30
const profileService = {31
get: () => ({ id: "vm", label: "vm", host: env.CHJ_VM_HOST, port: 22, username: env.CHJ_VM_USER || "ai", authMethod: "privateKey", privateKeyPath: env.CHJ_VM_KEY }),32
getHostKey: (h, p) => hostKeys.get(`${h}:${p}`) || null, trustHostKey: (h, p, fp) => hostKeys.set(`${h}:${p}`, fp), getStoredPassword: () => "", markUsed() {}33
};34
const manager = new SessionManager({ profileService });35
const connect = async () => {36
try { await manager.connect({ sessionId: "vm", profileId: "vm" }); }37
catch (error) {38
if (error.code !== "HOST_KEY_UNKNOWN") throw error;39
assert.equal(`SHA256:${Buffer.from(error.fingerprint, "hex").toString("base64").replace(/=+$/, "")}`, env.CHJ_VM_FINGERPRINT, "documented host key");40
manager.trustPending({ sessionId: "vm", profileId: "vm", fingerprint: error.fingerprint });41
await manager.connect({ sessionId: "vm", profileId: "vm" });42
}43
};44
await connect();45
const executor = new PrivilegedExecutor({ sessionManager: manager });46
const disks = new DiskService({ sessionManager: manager, privilegedExecutor: executor });47
const auth = { sudoPassword };48
const fixture = async (...args) => {49
const run = await executor.run("vm", { script: FIXTURE, args, privileged: true, sudoPassword, purpose: "test.disks.fixture", timeoutMs: 300000, errorPrefix: "FIXTURE_" });50
return Object.fromEntries(run.stdout.split("\n").filter((line) => line.includes("=")).map((line) => [line.slice(0, line.indexOf("=")), line.slice(line.indexOf("=") + 1)]));51
};52
const fixtureText = async (...args) => (await executor.run("vm", { script: FIXTURE, args, privileged: true, sudoPassword, purpose: "test.disks.fixture", timeoutMs: 300000, errorPrefix: "FIXTURE_" })).stdout.trim();53
let wrote = false;54
t.after(async () => {55
if (wrote) { try { t.diagnostic(`cleanup: ${JSON.stringify(await fixture("cleanup", RUN))}`); } catch (error) { t.diagnostic(`cleanup failed: ${error.code} ${error.message}`); } }56
await manager.disconnectAll("test");57
});59
// ---------- read-only ----------60
let rootInventory;61
await t.test("read-only inventory without root", async () => {62
const inventory = await disks.inspect("vm");63
assert.equal(inventory.supported, true); assert.equal(inventory.platform, "linux"); assert.equal(inventory.privileged, false);64
assert.ok(inventory.disks.length >= 1, "at least one disk");65
const system = inventory.disks.find((disk) => disk.systemRoles.includes("mount:/"));66
assert.ok(system, "the disk holding / is identified without root");67
t.diagnostic(`${inventory.os} | kernel ${inventory.kernel} | systemd ${inventory.systemd} | lsblk ${inventory.sources.lsblk} | disks ${inventory.disks.map((disk) => `${disk.path}:${disk.mediaType}/${disk.transport}/${disk.table?.type}`).join(", ")}`);68
});69
await t.test("privileged inventory, tools and suggestions", async () => {70
rootInventory = await disks.inspect("vm", { privileged: true, ...auth });71
assert.equal(rootInventory.privileged, true);72
const system = rootInventory.disks.find((disk) => disk.systemRoles.includes("mount:/"));73
assert.ok(system.table?.hash, "partition table read with sfdisk");74
const root = rootInventory.nodes.find((node) => node.mountpoints.includes("/"));75
assert.ok(root.fstype && root.uuid, "root file system type and UUID");76
assert.ok(root.usage?.size > 0 && root.usage.used > 0, "root usage from df");77
for (const part of system.partitions) if (part.kernel) assert.ok(part.uuid, `PARTUUID of ${part.path}`);78
const tools = ["sfdisk", "partx", "growpart", "resize2fs", "xfs_growfs", "btrfs", "pvresize", "cryptsetup", "smartctl", "parted", "systemd-run", "setsid"].map((name) => `${name}:${rootInventory.tools[name] ? "yes" : "no"}`).join(" ");79
t.diagnostic(`tools ${tools}`);80
t.diagnostic(`root ${root.path} ${root.fstype} on ${system.path}; suggestions: ${rootInventory.suggestions.map((entry) => `${entry.id}=${entry.available ? "available" : entry.blocked.code}`).join(", ") || "none"}`);81
for (const suggestion of rootInventory.suggestions.filter((entry) => entry.target.startsWith(system.path))) assert.ok(suggestion.available || suggestion.blocked, "system disk suggestions are explicit");82
});83
await t.test("system disk is protected against destructive plans", async () => {84
const system = rootInventory.disks.find((disk) => disk.systemRoles.includes("mount:/"));85
const rootPart = system.partitions.find((part) => part.mountpoints.includes("/"));86
if (rootPart) {87
await assert.rejects(() => disks.plan("vm", { operations: [{ type: "delete-partition", disk: system.name, number: rootPart.number }], ...auth }), { code: "DISKS_DEVICE_IN_USE" });88
await assert.rejects(() => disks.plan("vm", { operations: [{ type: "unmount", device: rootPart.path }], ...auth }), { code: "DISKS_SYSTEM_MOUNT" });89
if (rootPart.fstype === "xfs") await assert.rejects(() => disks.plan("vm", { operations: [{ type: "resize-partition", disk: system.name, number: rootPart.number, size: Math.floor(rootPart.size / 2) }], ...auth }), { code: "DISKS_XFS_SHRINK_UNSUPPORTED" });90
else await assert.rejects(() => disks.plan("vm", { operations: [{ type: "resize-partition", disk: system.name, number: rootPart.number, size: Math.floor(rootPart.size / 2) }], ...auth }), { code: /^DISKS_(SHRINK_REQUIRES_UNMOUNT|PROTECTED_DEVICE)$/ });91
}92
await assert.rejects(() => disks.plan("vm", { operations: [{ type: "create-table", disk: system.name, label: "gpt" }], ...auth }), { code: /^DISKS_(DEVICE_IN_USE|PROTECTED_DEVICE)$/ });93
const after = await disks.inspect("vm", { privileged: true, ...auth });94
assert.equal(after.disks.find((disk) => disk.name === system.name).table.hash, system.table.hash, "the system disk is unchanged");95
});96
await t.test("wrong sudo password and SMART on a virtual disk", async () => {97
executor.forgetPassword("vm");98
await assert.rejects(() => disks.inspect("vm", { privileged: true, sudoPassword: "definitely-wrong-password" }), { code: "SUDO_PASSWORD_INCORRECT" });99
const system = rootInventory.disks.find((disk) => disk.systemRoles.includes("mount:/"));100
const smart = await disks.smart("vm", { device: system.path, ...auth });101
t.diagnostic(`SMART ${system.path}: available=${smart.available} reason=${smart.reason} tool=${smart.tool}`);102
assert.equal(typeof smart.available, "boolean");103
});105
if (env.CHJ_DISKS_WRITE !== "1") { t.diagnostic("CHJ_DISKS_WRITE is not 1: destructive loop-device tests skipped"); return; }106
if (rootInventory.pve) { t.diagnostic("Proxmox VE host: destructive tests are never run on Proxmox hosts (read-only only)"); return; }107
if (!rootInventory.tools.losetup && !rootInventory.tools.sfdisk) { t.diagnostic("losetup/sfdisk missing: destructive tests BLOCKED"); return; }109
// ---------- destructive, dedicated loop images only ----------110
const has = (tool) => Boolean(rootInventory.tools[tool]);111
const fresh = () => disks.inspect("vm", { privileged: true, ...auth });112
const assertTestDisk = (inventory, name) => {113
const disk = inventory.disks.find((entry) => entry.name === name);114
assert.ok(disk, `${name} present`); assert.equal(disk.kind, "loop"); assert.match(disk.loopBacking || "", TEST_IMAGE, "target is a CH-J test image");115
assert.equal(disk.system, false); return disk;116
};117
const runJob = async (plan, { disconnect = false } = {}) => {118
for (const view of plan.before) { if (!/^\/dev\/loop\d+$/.test(view.path)) throw new Error(`refusing to apply a plan touching ${view.path}`); assertTestDisk(await fresh(), view.name); }119
const started = await disks.apply("vm", { planId: plan.planId, confirm: true, ...auth, rememberSudo: true });120
if (disconnect) {121
await manager.disconnect("vm", "test: simulated SSH outage");122
await new Promise((resolve) => setTimeout(resolve, 1500));123
await connect();124
}125
let status;126
for (let i = 0; i < 400; i += 1) {127
status = await disks.jobStatus("vm", { jobId: started.jobId, offset: 0, ...auth });128
if (status.outcome !== "running") break;129
await new Promise((resolve) => setTimeout(resolve, 750));130
}131
if (status.outcome !== "succeeded") t.diagnostic(`job ${started.jobId} ${status.outcome}: ${JSON.stringify(status.error)}\n${status.log.slice(-3000)}`);132
return { ...status, started };133
};134
const plan = (operations) => disks.plan("vm", { operations, ...auth });135
const mountpoint = (name) => `/mnt/chj-disks-test-${RUN}${name}`;137
wrote = true;138
const image = await fixture("create", RUN, "1024");139
const loop = image.dev.slice(5);140
t.diagnostic(`test image ${image.img} on ${image.dev}`);141
let sums;143
await t.test("GPT + ext4 partition + mount + data", async () => {144
let inventory = await fresh(); assertTestDisk(inventory, loop);145
const job = await runJob(await plan([{ type: "create-table", disk: loop, label: "gpt" }, { type: "create-partition", disk: loop, start: 2048, size: 512 * MiB / 512, fs: "ext4", label: "chjtest" }, { type: "mount", device: `${image.dev}p1`, mountpoint: mountpoint("a") }]));146
assert.equal(job.outcome, "succeeded"); assert.match(job.log, /NOT a backup of the data|has no partition table/);147
inventory = await fresh();148
const part = assertTestDisk(inventory, loop).partitions[0];149
assert.equal(part.fstype, "ext4"); assert.equal(part.label, "chjtest"); assert.deepEqual(part.mountpoints, [mountpoint("a")]);150
sums = await fixtureText("write", mountpoint("a"));151
});153
await t.test("virtual disk enlarged: detected, auto-expanded online (ext4 mounted), data intact", async () => {154
await fixture("grow", image.dev, "2048");155
const inventory = await fresh();156
const disk = assertTestDisk(inventory, loop);157
// Newer sfdisk reports the old last LBA (CH-J adds gpt-relocate); sfdisk 2.34 already reports the new one and158
// moves the backup header itself when writing. Either way the whole new space must be usable.159
const relocate = disk.table.gptBackupMisplaced;160
if (!relocate) { assert.equal(disk.table.lastUsableNow, disk.table.lastUsable); t.diagnostic("sfdisk reports the enlarged GPT area already (relocation on write)"); }161
const suggestion = inventory.suggestions.find((entry) => entry.id === `partition:${loop}:1`);162
assert.ok(suggestion?.available, "auto-expand offered"); assert.ok(suggestion.freeBytes > 1400 * MiB);163
const expand = await disks.autoExpand("vm", { suggestionId: suggestion.id, ...auth });164
assert.deepEqual(expand.steps.map((step) => step.name).filter((name) => !/^verify|^backup/.test(name)), [...(relocate ? ["gpt-relocate"] : []), "resize-part", "grow-fs"]);165
const job = await runJob(expand);166
assert.equal(job.outcome, "succeeded");167
const after = assertTestDisk(await fresh(), loop);168
const part = after.partitions[0];169
assert.ok(part.bytes > 1900 * MiB, "partition grew"); assert.ok(part.usage.size > 1800 * MiB, "mounted ext4 grew online");170
assert.equal(after.table.gptBackupMisplaced, false);171
assert.equal(await fixtureText("sums", mountpoint("a")), sums, "files unchanged");172
});174
await t.test("offline ext4 shrink (unmount, e2fsck, minimum + reserve, fs before partition) with verified data", async () => {175
await assert.rejects(() => plan([{ type: "resize-partition", disk: loop, number: 1, size: 768 * MiB / 512 }]), { code: "DISKS_SHRINK_REQUIRES_UNMOUNT" });176
await assert.rejects(() => plan([{ type: "unmount", device: `${image.dev}p1` }, { type: "resize-partition", disk: loop, number: 1, size: 64 * MiB / 512 }]), { code: "DISKS_SIZE_TOO_SMALL" });177
const job = await runJob(await plan([{ type: "unmount", device: `${image.dev}p1` }, { type: "resize-partition", disk: loop, number: 1, size: 768 * MiB / 512 }, { type: "mount", device: `${image.dev}p1`, mountpoint: mountpoint("a") }]));178
assert.equal(job.outcome, "succeeded");179
assert.match(job.log, /consistency check before shrinking/); assert.match(job.log, /verified: e2fsck -n clean/);180
const part = assertTestDisk(await fresh(), loop).partitions[0];181
assert.equal(part.bytes, 768 * MiB); assert.equal(await fixtureText("sums", mountpoint("a")), sums, "files unchanged after shrink");182
});184
await t.test("stale plan: a layout change between review and apply is refused before any change", async () => {185
const reviewed = await plan([{ type: "create-partition", disk: loop, start: 2048 + 768 * MiB / 512, size: 64 * MiB / 512, fs: "ext4" }]);186
await fixture("tamper", image.dev);187
await assert.rejects(() => disks.apply("vm", { planId: reviewed.planId, confirm: true, ...auth }), { code: "DISKS_PLAN_STALE" });188
const disk = assertTestDisk(await fresh(), loop);189
assert.equal(disk.partitions.length, 2, "only the out-of-band partition exists; the plan changed nothing");190
const extra = disk.partitions.find((part) => part.number === 2);191
assert.equal((await runJob(await plan([{ type: "delete-partition", disk: loop, number: extra.number }]))).outcome, "succeeded");192
});194
if (has("mkfs.xfs") && has("xfs_growfs")) {195
await t.test("XFS: create, online grow, shrink refused", async () => {196
const start = 2048 + 768 * MiB / 512;197
assert.equal((await runJob(await plan([{ type: "create-partition", disk: loop, start, size: 320 * MiB / 512, fs: "xfs" }, { type: "mount", device: `${image.dev}p2`, mountpoint: mountpoint("x") }]))).outcome, "succeeded");198
const grow = await plan([{ type: "resize-partition", disk: loop, number: 2, size: 512 * MiB / 512 }]);199
assert.deepEqual(grow.steps.map((step) => step.name).filter((name) => !/^verify|^backup/.test(name)), ["resize-part", "grow-fs"]);200
assert.equal((await runJob(grow)).outcome, "succeeded");201
try {202
const part = assertTestDisk(await fresh(), loop).partitions.find((entry) => entry.number === 2);203
assert.equal(part.bytes, 512 * MiB);204
assert.equal(part.fsSize, 512 * MiB, "XFS data section grew online to the partition size (xfs_info)");205
await assert.rejects(() => plan([{ type: "resize-partition", disk: loop, number: 2, size: 320 * MiB / 512 }]), { code: "DISKS_XFS_SHRINK_UNSUPPORTED" });206
} finally {207
assert.equal((await runJob(await plan([{ type: "unmount", device: `${image.dev}p2` }, { type: "delete-partition", disk: loop, number: 2 }]))).outcome, "succeeded");208
}209
});210
} else t.diagnostic("XFS tools missing: XFS scenario UNSUPPORTED on this VM");212
if (has("pvresize") && has("lvextend")) {213
await t.test("LVM on a partition: partition -> PV -> LV -> ext4 auto expansion", async () => {214
const start = 2048 + 768 * MiB / 512;215
assert.equal((await runJob(await plan([{ type: "create-partition", disk: loop, start, size: 300 * MiB / 512, fs: "none" }]))).outcome, "succeeded");216
const vg = `chjtest${RUN}`;217
await fixture("lvm", `${image.dev}p2`, vg);218
const inventory = await fresh();219
const suggestion = inventory.suggestions.find((entry) => entry.id === `partition:${loop}:2`);220
assert.ok(suggestion?.available, `auto-expand offered (${suggestion?.blocked?.code || ""})`);221
const expand = await disks.autoExpand("vm", { suggestionId: suggestion.id, ...auth });222
assert.deepEqual(expand.steps.map((step) => step.name).filter((name) => !/^verify|^backup/.test(name)), ["resize-part", "pv-resize", "lv-extend", "grow-fs"]);223
assert.equal((await runJob(expand)).outcome, "succeeded");224
const after = await fresh();225
const lv = after.lvm.lvs.find((entry) => entry.vg === vg);226
assert.ok(lv.size > 1000 * MiB, `LV grew (${lv.size})`);227
const node = after.nodes.find((entry) => entry.displayPath === `/dev/${vg}/data`);228
assert.ok(node.fsSize > 1000 * MiB, "ext4 inside the LV grew");229
});230
} else t.diagnostic("LVM tools missing: LVM scenario UNSUPPORTED on this VM");232
await t.test("SSH outage during a job: the job continues, status is recovered, nothing runs twice", async () => {233
const reviewed = await plan([{ type: "unmount", device: `${image.dev}p1` }, { type: "check", device: `${image.dev}p1` }, { type: "mount", device: `${image.dev}p1`, mountpoint: mountpoint("a") }]);234
const job = await runJob(reviewed, { disconnect: true });235
assert.equal(job.outcome, "succeeded", "finished on the server despite the dropped SSH connection");236
const { jobs } = await disks.jobs("vm", auth);237
assert.ok(jobs.some((entry) => entry.id === job.started.jobId && entry.outcome === "succeeded"), "job listed after reconnect");238
await assert.rejects(() => disks.apply("vm", { planId: reviewed.planId, confirm: true, ...auth }), { code: "DISKS_PLAN_EXPIRED" });239
await assert.rejects(() => executor.run("vm", { script: disks.script, args: ["job-start", job.started.jobId], privileged: true, sudoPassword, frames: [{ tag: "PLAN", data: "verify-size /dev/null 0\n" }, { tag: "HELPER", data: "#" }, { tag: "TITLE", data: "x" }], errorPrefix: "DISKS_" }), { code: "DISKS_JOB_EXISTS" });240
assert.equal(await fixtureText("sums", mountpoint("a")), sums);241
});243
await t.test("a running disk job blocks every other disk job on the server (lock contention)", async () => {244
const reviewed = await plan([{ type: "set-label", device: `${image.dev}p1`, label: "chjlock" }]);245
await fixture("hold-lock", RUN);246
try { await assert.rejects(() => disks.apply("vm", { planId: reviewed.planId, confirm: true, ...auth }), { code: "DISKS_BUSY" }); }247
finally { await fixture("release-lock", RUN); }248
const retry = await plan([{ type: "set-label", device: `${image.dev}p1`, label: "chjlock" }]);249
assert.equal((await runJob(retry)).outcome, "succeeded", "works again once the lock is free");250
});252
await t.test("conflicting jobs never overlap on the server", async () => {253
const first = await plan([{ type: "unmount", device: `${image.dev}p1` }, { type: "check", device: `${image.dev}p1` }]);254
const second = await plan([{ type: "set-label", device: `${image.dev}p1`, label: "chjb" }]);255
const a = await disks.apply("vm", { planId: first.planId, confirm: true, ...auth });256
let b = null; let bError = null;257
try { b = await disks.apply("vm", { planId: second.planId, confirm: true, ...auth }); } catch (error) { bError = error; }258
const wait = async (jobId) => { for (let i = 0; i < 200; i += 1) { const status = await disks.jobStatus("vm", { jobId, ...auth }); if (status.outcome !== "running") return status; await new Promise((resolve) => setTimeout(resolve, 500)); } return null; };259
const sa = await wait(a.jobId); const sb = b ? await wait(b.jobId) : null;260
t.diagnostic(`first ${sa.outcome}; second ${bError ? bError.code : `${sb.outcome} ${sb.error?.code || ""}`}`);261
if (bError) assert.ok(["DISKS_BUSY", "DISKS_PLAN_STALE"].includes(bError.code));262
else if (sb.outcome === "failed") assert.match(sb.error.code, /DISKS_(BUSY|DEVICE_IN_USE|LAYOUT_CHANGED)/);263
else assert.ok(Date.parse(sb.started || sb.created) >= Date.parse(sa.finished), "second job started only after the first finished");264
});266
if (has("cryptsetup")) {267
await t.test("LUKS with ext4: keyring mapping is refused, dm-crypt mapping grows disk -> partition -> LUKS -> ext4", async () => {268
const second = await fixture("create", `${RUN}l`, "512");269
const name = second.dev.slice(5);270
assert.equal((await runJob(await plan([{ type: "create-table", disk: name, label: "gpt" }, { type: "create-partition", disk: name, start: 2048, size: 128 * MiB / 512, fs: "none" }]))).outcome, "succeeded");271
const luks = await fixture("luks", `${second.dev}p1`, `chjtest${RUN}k`, "dm-crypt");272
t.diagnostic(`LUKS key location ${luks.keylocation}`);273
const inventory = await fresh();274
const suggestion = inventory.suggestions.find((entry) => entry.id === `partition:${name}:1`);275
if (/keyring/.test(luks.keylocation)) { assert.equal(suggestion.blocked.code, "DISKS_LUKS_PASSPHRASE_REQUIRED"); return; }276
assert.ok(suggestion?.available, `auto-expand offered (${suggestion?.blocked?.code || ""})`);277
const expand = await disks.autoExpand("vm", { suggestionId: suggestion.id, ...auth });278
assert.deepEqual(expand.steps.map((step) => step.name).filter((name) => !/^verify|^backup/.test(name)), ["resize-part", "luks-resize", "grow-fs"]);279
assert.equal((await runJob(expand)).outcome, "succeeded");280
const map = (await fresh()).nodes.find((node) => node.path === luks.map);281
assert.ok(map.size > 400 * MiB && map.fsSize > 400 * MiB, "mapping and ext4 grew");282
const keyring = await fixture("create", `${RUN}m`, "256");283
assert.equal((await runJob(await plan([{ type: "create-table", disk: keyring.dev.slice(5), label: "gpt" }, { type: "create-partition", disk: keyring.dev.slice(5), start: 2048, size: 64 * MiB / 512, fs: "none" }]))).outcome, "succeeded");284
const second2 = await fixture("luks", `${keyring.dev}p1`, `chjtest${RUN}n`, "keyring");285
const blocked = (await fresh()).suggestions.find((entry) => entry.id === `partition:${keyring.dev.slice(5)}:1`);286
if (/keyring/.test(second2.keylocation)) assert.equal(blocked.blocked.code, "DISKS_LUKS_PASSPHRASE_REQUIRED");287
else t.diagnostic(`cryptsetup used ${second2.keylocation} even without --disable-keyring`);288
});289
} else t.diagnostic("cryptsetup missing: LUKS scenario UNSUPPORTED on this VM");290
});SHA-256
e7be9a72072595f412df939080d381ce0194ef1e2b92f9ec6b78bbaca6005955CH-J Proprietary Software License 1.14 · Die Quellen werden unter der CH-J Proprietary Software License 1.14 bereitgestellt. Ihre Verfügbarkeit ändert die Lizenzbedingungen nicht und gewährt keine zusätzlichen Rechte.
SHA-256 des Archivs
476ed8aef0f1e96a5acdf223127b6692acdf4492eeaf5d0816b6e2a634fbbda6