feat(storage): add verified offline MinIO migration pilot
This commit is contained in:
39
scripts/storage/cli.mjs
Normal file
39
scripts/storage/cli.mjs
Normal file
@@ -0,0 +1,39 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { client, call, inventory, transfer, contract, verifyManifest, atomicJson, cleanupProbe } from './s3.mjs';
|
||||
|
||||
// Secrets enter through a root-only mounted JSON file, never through argv or logs.
|
||||
const [command, configPath = '/config/job.json'] = process.argv.slice(2);
|
||||
if (command === 'contract') process.env.AWS_REQUEST_CHECKSUM_CALCULATION = 'WHEN_REQUIRED';
|
||||
let source, target;
|
||||
try {
|
||||
const config = JSON.parse(await readFile(configPath, 'utf8'));
|
||||
source = config.source && client(config.source);
|
||||
target = config.target && client(config.target);
|
||||
const bucket = config.bucket;
|
||||
let result;
|
||||
if (command === 'inventory') result = await inventory(source, bucket);
|
||||
else if (command === 'copy' || command === 'verify') {
|
||||
result = await transfer(source, target, bucket, config.directory || '/work', { verifyOnly: command === 'verify' });
|
||||
result = { ...result, objects: undefined };
|
||||
} else if (command === 'init') {
|
||||
try { await call(target, 'HeadBucket', { Bucket: bucket }); }
|
||||
catch (error) {
|
||||
if (error.$metadata?.httpStatusCode !== 404) throw error;
|
||||
await call(target, 'CreateBucket', { Bucket: bucket });
|
||||
}
|
||||
result = { bucket, initialized: true };
|
||||
} else if (command === 'contract') result = await contract(target, config.target, bucket, { ...config, probePath: '/work/probe.json' });
|
||||
else if (command === 'cleanup-probe') { await cleanupProbe(target, bucket, '/work/probe.json'); result = { cleaned: true }; }
|
||||
else if (command === 'restore-verify') {
|
||||
result = await verifyManifest(target, bucket, JSON.parse(await readFile(config.manifest || '/work/verified.json', 'utf8')));
|
||||
} else throw new Error('Unbekannter Befehl.');
|
||||
await atomicJson(config.result || '/work/result.json', result);
|
||||
console.log(JSON.stringify(command === 'inventory' ? { count: result.count, bytes: result.bytes } : result));
|
||||
} catch (error) {
|
||||
// SDK error messages/URLs may contain identifiers; keep public output bounded.
|
||||
const detail = { error: error.name, code: error.code, status: error.$metadata?.httpStatusCode,
|
||||
message: error.$metadata ? 'S3-Prüfung fehlgeschlagen; Zugriff/Konfiguration prüfen.' : error.message };
|
||||
try { await atomicJson('/work/error.json', detail); } catch { /* Preserve the original error. */ }
|
||||
console.error(JSON.stringify(detail));
|
||||
process.exitCode = 1;
|
||||
} finally { source?.destroy(); target?.destroy(); }
|
||||
Reference in New Issue
Block a user