@celilo/cli 2.2.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/schemas/system_config.json +5 -0
- package/src/ansible/inventory.test.ts +2 -1
- package/src/api/sessions.test.ts +2 -1
- package/src/cli/backup-rename.test.ts +2 -1
- package/src/cli/cli.test.ts +2 -1
- package/src/cli/commands/console-get-chain.test.ts +2 -1
- package/src/cli/commands/firewall-interface-list.test.ts +2 -1
- package/src/cli/commands/module-config.test.ts +2 -1
- package/src/cli/commands/module-deploy.ts +8 -2
- package/src/cli/commands/module-generate.test.ts +53 -0
- package/src/cli/commands/module-generate.ts +31 -26
- package/src/cli/commands/module-import-aspect.test.ts +2 -1
- package/src/cli/commands/module-import-registry.test.ts +2 -1
- package/src/cli/commands/module-operations.test.ts +2 -1
- package/src/cli/commands/module-publish.test.ts +5 -12
- package/src/cli/commands/module-update.test.ts +6 -5
- package/src/cli/commands/module-upgrade.test.ts +15 -0
- package/src/cli/commands/module-upgrade.ts +54 -2
- package/src/cli/commands/module-verify.test.ts +2 -3
- package/src/cli/commands/module-verify.ts +0 -1
- package/src/cli/commands/notify-config.test.ts +5 -3
- package/src/cli/commands/registry-owner.test.ts +2 -1
- package/src/cli/commands/registry-token.test.ts +2 -1
- package/src/cli/commands/system-apply-config-equivalence.test.ts +5 -7
- package/src/cli/commands/system-config.test.ts +148 -0
- package/src/cli/commands/system-config.ts +26 -1
- package/src/cli/commands/system-doctor.test.ts +71 -0
- package/src/cli/commands/system-doctor.ts +110 -24
- package/src/cli/commands/system-init-deprecation.test.ts +6 -3
- package/src/cli/commands/system-migrate.test.ts +2 -1
- package/src/cli/index.ts +2 -1
- package/src/cli/restore-command.test.ts +2 -1
- package/src/cli/restore-migration-failure.test.ts +2 -1
- package/src/config/paths.test.ts +3 -3
- package/src/db/migrate.test.ts +2 -1
- package/src/db/migrate.ts +16 -16
- package/src/hooks/capability-loader.test.ts +78 -0
- package/src/hooks/capability-loader.ts +37 -2
- package/src/hooks/executor.test.ts +87 -1
- package/src/hooks/executor.ts +57 -21
- package/src/hooks/hook-store.test.ts +6 -0
- package/src/hooks/hook-trespass.test.ts +17 -7
- package/src/hooks/jail.test.ts +61 -2
- package/src/hooks/jail.ts +53 -1
- package/src/module/packaging/audit.ts +9 -26
- package/src/module/packaging/build-paths.test.ts +127 -0
- package/src/module/packaging/build-paths.ts +175 -0
- package/src/module/packaging/build.test.ts +71 -1
- package/src/module/packaging/build.ts +60 -0
- package/src/module/packaging/extract.ts +1 -5
- package/src/policy/module-script-scan.ts +0 -40
- package/src/policy/no-swallowed-refusal.test.ts +9 -10
- package/src/services/alerting/ack.test.ts +2 -1
- package/src/services/alerting/cadence-migration.test.ts +3 -2
- package/src/services/alerting/coverage-source.test.ts +2 -1
- package/src/services/alerting/deferral.test.ts +2 -1
- package/src/services/alerting/delivery-loop.test.ts +2 -1
- package/src/services/alerting/deploy-hooks.test.ts +2 -1
- package/src/services/alerting/inbound-poller.test.ts +2 -1
- package/src/services/alerting/inbound.test.ts +2 -1
- package/src/services/alerting/notification-responder.test.ts +2 -1
- package/src/services/alerting/run-monitor.test.ts +2 -1
- package/src/services/alerting/store.test.ts +2 -1
- package/src/services/alerting/sweep-runner.test.ts +2 -1
- package/src/services/alerting/tokens.test.ts +2 -1
- package/src/services/aspect-approvals.test.ts +2 -1
- package/src/services/aspect-reconcile.test.ts +4 -3
- package/src/services/aspect-runner.test.ts +2 -1
- package/src/services/audit/module-integrity.test.ts +0 -21
- package/src/services/audit/module-integrity.ts +0 -14
- package/src/services/backup-age-agreement.test.ts +2 -1
- package/src/services/backup-envelope-roundtrip.test.ts +2 -1
- package/src/services/backup-in-flight-refusal.test.ts +2 -1
- package/src/services/bus-ensure-flow.test.ts +2 -1
- package/src/services/bus-interview-park.test.ts +2 -1
- package/src/services/bus-secret-flow.test.ts +2 -1
- package/src/services/capability-table-rows.test.ts +2 -1
- package/src/services/consumer-cleanup.test.ts +3 -2
- package/src/services/container-service.test.ts +2 -1
- package/src/services/control-plane-bootstrap.test.ts +2 -1
- package/src/services/cross-module-read.test.ts +2 -1
- package/src/services/deploy-validation.test.ts +3 -2
- package/src/services/dns-internal-records.test.ts +3 -2
- package/src/services/dns-provider-backfill.test.ts +2 -1
- package/src/services/dns-registrations.test.ts +2 -1
- package/src/services/ensure-interview.test.ts +3 -2
- package/src/services/fleet-checks.test.ts +3 -2
- package/src/services/fleet-key.test.ts +3 -2
- package/src/services/infrastructure-selector.test.ts +2 -1
- package/src/services/infrastructure-variable-resolver.test.ts +2 -1
- package/src/services/machine-pool.test.ts +2 -1
- package/src/services/module-config.test.ts +2 -1
- package/src/services/module-deploy.dns-repoint.test.ts +2 -1
- package/src/services/module-deploy.ts +36 -20
- package/src/services/module-operations.test.ts +2 -1
- package/src/services/module-subscriptions.test.ts +2 -1
- package/src/services/port-forwards.test.ts +2 -1
- package/src/services/programmatic-responder.aspect.test.ts +2 -1
- package/src/services/proxmox-reconcile.test.ts +2 -1
- package/src/services/restore-from-file.test.ts +3 -2
- package/src/services/restore-preflight.test.ts +2 -1
- package/src/services/secret-schema-loader.test.ts +2 -1
- package/src/services/ssh-key-manager.test.ts +2 -1
- package/src/services/static-content-converge.test.ts +5 -4
- package/src/services/static-content-converge.ts +33 -10
- package/src/services/system-state-stage.test.ts +2 -1
- package/src/services/trusted-sources.test.ts +3 -2
- package/src/services/zone-detector.test.ts +2 -1
- package/src/test-utils/bus-responder.ts +5 -3
- package/src/test-utils/db-path.ts +25 -0
- package/src/test-utils/integration.ts +7 -0
- package/src/test-utils/module-fixtures.ts +5 -6
- package/src/module/packaging/generated-plane.test.ts +0 -79
- package/src/module/packaging/generated-plane.ts +0 -134
|
@@ -6,6 +6,7 @@ import type { DnsRecordRequest, HookLogger } from '@celilo/capabilities';
|
|
|
6
6
|
import { closeDb, getDb } from '../db/client';
|
|
7
7
|
import { runMigrations } from '../db/migrate';
|
|
8
8
|
import { capabilities, moduleConfigs, modules, webRoutes } from '../db/schema';
|
|
9
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
9
10
|
import { backfillWebRouteDns } from './dns-provider-backfill';
|
|
10
11
|
|
|
11
12
|
const silentLogger: HookLogger = {
|
|
@@ -26,7 +27,7 @@ describe('backfillWebRouteDns (ISS-0029)', () => {
|
|
|
26
27
|
|
|
27
28
|
afterEach(() => {
|
|
28
29
|
closeDb();
|
|
29
|
-
|
|
30
|
+
resetTestDbPath();
|
|
30
31
|
try {
|
|
31
32
|
rmSync(dir, { recursive: true, force: true });
|
|
32
33
|
} catch {
|
|
@@ -14,6 +14,7 @@ import type { DnsRegistrarCapability } from '@celilo/capabilities';
|
|
|
14
14
|
import { eq } from 'drizzle-orm';
|
|
15
15
|
import { type DbClient, getDb } from '../db/client';
|
|
16
16
|
import { modules } from '../db/schema';
|
|
17
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
17
18
|
import {
|
|
18
19
|
listDnsRegistrations,
|
|
19
20
|
recordDnsRegistration,
|
|
@@ -44,7 +45,7 @@ describe('dns_registrations ledger', () => {
|
|
|
44
45
|
|
|
45
46
|
afterEach(() => {
|
|
46
47
|
rmSync(tempDir, { recursive: true, force: true });
|
|
47
|
-
|
|
48
|
+
resetTestDbPath();
|
|
48
49
|
});
|
|
49
50
|
|
|
50
51
|
test('record + list roundtrip; a re-assert is an upsert, not a second row', () => {
|
|
@@ -8,6 +8,7 @@ import { moduleConfigs, modules, secrets } from '../db/schema';
|
|
|
8
8
|
import type { Ensure } from '../manifest/schema';
|
|
9
9
|
import { decryptSecret } from '../secrets/encryption';
|
|
10
10
|
import { getOrCreateMasterKey } from '../secrets/master-key';
|
|
11
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
11
12
|
import {
|
|
12
13
|
findEnsureOnProvider,
|
|
13
14
|
interviewForEnsureInputs,
|
|
@@ -56,7 +57,7 @@ describe('interviewForEnsureInputs', () => {
|
|
|
56
57
|
|
|
57
58
|
afterEach(() => {
|
|
58
59
|
rmSync(tempDir, { recursive: true, force: true });
|
|
59
|
-
|
|
60
|
+
resetTestDbPath();
|
|
60
61
|
});
|
|
61
62
|
|
|
62
63
|
test('appends to array config and sets secret object key', async () => {
|
|
@@ -185,7 +186,7 @@ describe('findEnsureOnProvider', () => {
|
|
|
185
186
|
|
|
186
187
|
afterEach(() => {
|
|
187
188
|
rmSync(tempDir, { recursive: true, force: true });
|
|
188
|
-
|
|
189
|
+
resetTestDbPath();
|
|
189
190
|
});
|
|
190
191
|
|
|
191
192
|
test('finds ensure by id on a registered provider', () => {
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from '../db/schema';
|
|
15
15
|
import type { ModuleManifest } from '../manifest/schema';
|
|
16
16
|
import { setupTestDatabaseAt } from '../test-utils/database';
|
|
17
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
17
18
|
import { ensureInboundSubscriber, ensureSweepSubscriber } from './alerting/monitors';
|
|
18
19
|
import { ensureBackupSweepSubscriber } from './backup-sweep';
|
|
19
20
|
import { getDaemonUnitPath } from './events-daemon';
|
|
@@ -269,7 +270,7 @@ describe('checkSubscribers + checkCapabilityProviders', () => {
|
|
|
269
270
|
afterEach(() => {
|
|
270
271
|
bus.close();
|
|
271
272
|
db.$client.close();
|
|
272
|
-
|
|
273
|
+
resetTestDbPath();
|
|
273
274
|
process.env.EVENT_BUS_DB = undefined;
|
|
274
275
|
try {
|
|
275
276
|
rmSync(dir, { recursive: true, force: true });
|
|
@@ -978,7 +979,7 @@ describe('runFleetChecks includes host liveness (#728)', () => {
|
|
|
978
979
|
afterEach(() => {
|
|
979
980
|
bus.close();
|
|
980
981
|
db.$client.close();
|
|
981
|
-
|
|
982
|
+
resetTestDbPath();
|
|
982
983
|
process.env.EVENT_BUS_DB = undefined;
|
|
983
984
|
try {
|
|
984
985
|
rmSync(dir, { recursive: true, force: true });
|
|
@@ -9,6 +9,7 @@ import { execFileSync } from 'node:child_process';
|
|
|
9
9
|
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
10
10
|
import { tmpdir } from 'node:os';
|
|
11
11
|
import { join } from 'node:path';
|
|
12
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
12
13
|
import { ensureFleetKey, findFleetPrivateKey, getFleetSshDir } from './fleet-key';
|
|
13
14
|
|
|
14
15
|
describe('ensureFleetKey', () => {
|
|
@@ -20,7 +21,7 @@ describe('ensureFleetKey', () => {
|
|
|
20
21
|
});
|
|
21
22
|
|
|
22
23
|
afterEach(() => {
|
|
23
|
-
|
|
24
|
+
resetTestDbPath();
|
|
24
25
|
rmSync(dataDir, { recursive: true, force: true });
|
|
25
26
|
});
|
|
26
27
|
|
|
@@ -61,7 +62,7 @@ describe('findFleetPrivateKey', () => {
|
|
|
61
62
|
});
|
|
62
63
|
|
|
63
64
|
afterEach(() => {
|
|
64
|
-
|
|
65
|
+
resetTestDbPath();
|
|
65
66
|
process.env.HOME = realHome;
|
|
66
67
|
rmSync(dataDir, { recursive: true, force: true });
|
|
67
68
|
rmSync(homeDir, { recursive: true, force: true });
|
|
@@ -10,6 +10,7 @@ import { closeDb, createDbClient } from '../db/client';
|
|
|
10
10
|
import { runMigrations } from '../db/migrate';
|
|
11
11
|
import { modules } from '../db/schema';
|
|
12
12
|
import type { Module } from '../db/schema';
|
|
13
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
13
14
|
import { addContainerService } from './container-service';
|
|
14
15
|
import { InfrastructureError, selectInfrastructure } from './infrastructure-selector';
|
|
15
16
|
import { addMachine } from './machine-pool';
|
|
@@ -46,7 +47,7 @@ describe('infrastructure-selector', () => {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
// Clear environment variables
|
|
49
|
-
|
|
50
|
+
resetTestDbPath();
|
|
50
51
|
process.env.CELILO_MASTER_KEY_PATH = undefined;
|
|
51
52
|
});
|
|
52
53
|
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from '@/db/schema';
|
|
12
12
|
import type { ModuleManifest } from '@/manifest/schema';
|
|
13
13
|
import { and, eq } from 'drizzle-orm';
|
|
14
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
14
15
|
import { resolveInfrastructureVariables } from './infrastructure-variable-resolver';
|
|
15
16
|
import { upsertModuleConfig } from './module-config';
|
|
16
17
|
|
|
@@ -38,7 +39,7 @@ beforeEach(() => {
|
|
|
38
39
|
|
|
39
40
|
afterEach(async () => {
|
|
40
41
|
db.$client.close();
|
|
41
|
-
|
|
42
|
+
resetTestDbPath();
|
|
42
43
|
|
|
43
44
|
// Clean up database files
|
|
44
45
|
if (existsSync(TEST_DB_PATH)) {
|
|
@@ -10,6 +10,7 @@ import { eq } from 'drizzle-orm';
|
|
|
10
10
|
import { closeDb, createDbClient, getDb } from '../db/client';
|
|
11
11
|
import { runMigrations } from '../db/migrate';
|
|
12
12
|
import { machines, moduleInfrastructure, modules } from '../db/schema';
|
|
13
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
13
14
|
import {
|
|
14
15
|
addMachine,
|
|
15
16
|
getMachine,
|
|
@@ -52,7 +53,7 @@ describe('machine-pool', () => {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
// Clear environment variables
|
|
55
|
-
|
|
56
|
+
resetTestDbPath();
|
|
56
57
|
process.env.CELILO_MASTER_KEY_PATH = undefined;
|
|
57
58
|
});
|
|
58
59
|
|
|
@@ -9,6 +9,7 @@ import { join } from 'node:path';
|
|
|
9
9
|
import { closeDb, createDbClient } from '../db/client';
|
|
10
10
|
import { runMigrations } from '../db/migrate';
|
|
11
11
|
import { modules } from '../db/schema';
|
|
12
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
12
13
|
import {
|
|
13
14
|
formatConfigValue,
|
|
14
15
|
getAllModuleConfigValues,
|
|
@@ -59,7 +60,7 @@ describe('module-config service', () => {
|
|
|
59
60
|
afterEach(() => {
|
|
60
61
|
closeDb();
|
|
61
62
|
// Clean up environment variable
|
|
62
|
-
|
|
63
|
+
resetTestDbPath();
|
|
63
64
|
// Clean up test database
|
|
64
65
|
rmSync(testDir, { recursive: true, force: true });
|
|
65
66
|
});
|
|
@@ -9,6 +9,7 @@ import { eq } from 'drizzle-orm';
|
|
|
9
9
|
import { type DbClient, closeDb, getDb } from '../db/client';
|
|
10
10
|
import { runMigrations } from '../db/migrate';
|
|
11
11
|
import { capabilities, moduleSystems, modules, systemConfig } from '../db/schema';
|
|
12
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
12
13
|
import { InterviewUnansweredError } from './interview-errors';
|
|
13
14
|
import { planDnsRepoint, repointDnsPrimaryForProvider } from './module-deploy';
|
|
14
15
|
|
|
@@ -49,7 +50,7 @@ describe('repointDnsPrimaryForProvider (module-orchestrator-primitives slice 3)'
|
|
|
49
50
|
|
|
50
51
|
afterEach(() => {
|
|
51
52
|
closeDb();
|
|
52
|
-
|
|
53
|
+
resetTestDbPath();
|
|
53
54
|
try {
|
|
54
55
|
rmSync(dir, { recursive: true, force: true });
|
|
55
56
|
} catch {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Coordinates full module deployment workflow across all phases
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { rmSync } from 'node:fs';
|
|
7
8
|
import { join } from 'node:path';
|
|
8
9
|
import { ProgressDisplay } from '@celilo/cli-display';
|
|
9
10
|
import { and, eq } from 'drizzle-orm';
|
|
@@ -104,6 +105,17 @@ export interface DeployOptions {
|
|
|
104
105
|
* this mode — that requires a real hook run.
|
|
105
106
|
*/
|
|
106
107
|
stopAfterInterview?: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* D4 (control-plane-stops-building-modules): retain the generated project
|
|
110
|
+
* after a successful deploy instead of deleting it. The tree is ephemeral
|
|
111
|
+
* by design — rendered for this deploy from the signed payload plus live
|
|
112
|
+
* config, used, then removed — because a persisted generated tree cannot be
|
|
113
|
+
* validated (only its verbatim role assets have a meaningful digest, the
|
|
114
|
+
* rest is rendered and SUPPOSED to differ), it holds rendered secrets, and
|
|
115
|
+
* it is what the next deploy would ship. This flag keeps it for deliberate
|
|
116
|
+
* inspection. It is always kept when the deploy fails.
|
|
117
|
+
*/
|
|
118
|
+
keepGeneratedProject?: boolean;
|
|
107
119
|
}
|
|
108
120
|
|
|
109
121
|
/**
|
|
@@ -368,6 +380,26 @@ export async function deployModule(
|
|
|
368
380
|
closeDeployWindows(db, moduleId, new Date());
|
|
369
381
|
|
|
370
382
|
if (result.success) {
|
|
383
|
+
// D4 (control-plane-stops-building-modules): the generated project is
|
|
384
|
+
// ephemeral. It was rendered for THIS deploy from the signed payload plus
|
|
385
|
+
// live config; a persisted copy is a tree nothing can validate, it holds
|
|
386
|
+
// rendered secrets, and it is what a later deploy would ship. Now that
|
|
387
|
+
// the deploy that needed it is done — ansible, hooks, DNS backfill and
|
|
388
|
+
// aspects all ran inside the impl — it goes. Kept on failure (the impl's
|
|
389
|
+
// early returns) and under --keep.
|
|
390
|
+
if (!options.keepGeneratedProject) {
|
|
391
|
+
const deployed = db.select().from(modules).where(eq(modules.id, moduleId)).get();
|
|
392
|
+
if (deployed) {
|
|
393
|
+
const generatedPath = `${deployed.sourcePath}/generated`;
|
|
394
|
+
try {
|
|
395
|
+
rmSync(generatedPath, { recursive: true, force: true });
|
|
396
|
+
log.info(`Removed generated/ for '${moduleId}' (ephemeral by design; --keep retains it)`);
|
|
397
|
+
} catch (cleanupError) {
|
|
398
|
+
const msg = cleanupError instanceof Error ? cleanupError.message : String(cleanupError);
|
|
399
|
+
log.warn(`Failed to remove generated/ for '${moduleId}': ${msg}`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
371
403
|
// A module that declares a suggested interval starts being watched on its
|
|
372
404
|
// first successful deploy — watching is the default, not a thing to
|
|
373
405
|
// remember.
|
|
@@ -792,26 +824,10 @@ async function deployModuleImpl(
|
|
|
792
824
|
|
|
793
825
|
log.success('Templates generated');
|
|
794
826
|
|
|
795
|
-
//
|
|
796
|
-
//
|
|
797
|
-
//
|
|
798
|
-
//
|
|
799
|
-
// into a refusal at the point of the fault. A verbatim role asset that
|
|
800
|
-
// failed to refresh is invisible downstream: `cp` reports no error, Ansible
|
|
801
|
-
// copies the stale bytes and reports `ok`, unchanged, and the module's
|
|
802
|
-
// version advances past code that never shipped. Local, milliseconds, no
|
|
803
|
-
// SSH. It deliberately does NOT verify after the deploy — Ansible's
|
|
804
|
-
// `changed` is truthful about what Ansible did, and the lie is upstream of
|
|
805
|
-
// Ansible.
|
|
806
|
-
const { refuseIfGeneratedIsStale } = await import('../module/packaging/generated-plane');
|
|
807
|
-
const staleGenerated = await refuseIfGeneratedIsStale(
|
|
808
|
-
moduleId,
|
|
809
|
-
module.sourcePath,
|
|
810
|
-
generatedPath,
|
|
811
|
-
);
|
|
812
|
-
if (staleGenerated) {
|
|
813
|
-
return { success: false, error: staleGenerated, phases };
|
|
814
|
-
}
|
|
827
|
+
// The generated-tree staleness pre-flight (D6 of module-integrity-rigor)
|
|
828
|
+
// retired here (D5 of control-plane-stops-building-modules): the tree is
|
|
829
|
+
// rendered for THIS deploy and deleted when it succeeds, so nothing
|
|
830
|
+
// persists that could be stale relative to what ships.
|
|
815
831
|
|
|
816
832
|
// Run validate_config hook if defined (e.g., credential validation via Playwright)
|
|
817
833
|
if (manifest.hooks?.validate_config) {
|
|
@@ -5,6 +5,7 @@ import { tmpdir } from 'node:os';
|
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { closeDb } from '../db/client';
|
|
7
7
|
import { runMigrations } from '../db/migrate';
|
|
8
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
8
9
|
import {
|
|
9
10
|
InFlightError,
|
|
10
11
|
OPERATIONS_SWEEP_PATTERN,
|
|
@@ -30,7 +31,7 @@ describe('module-operations', () => {
|
|
|
30
31
|
|
|
31
32
|
afterEach(() => {
|
|
32
33
|
closeDb();
|
|
33
|
-
|
|
34
|
+
resetTestDbPath();
|
|
34
35
|
try {
|
|
35
36
|
rmSync(dir, { recursive: true, force: true });
|
|
36
37
|
} catch {
|
|
@@ -10,6 +10,7 @@ import { ModuleManifestSchema } from '../manifest/schema';
|
|
|
10
10
|
import { ModuleSubscriptionSchema } from '../manifest/schema';
|
|
11
11
|
import type { ModuleManifest } from '../manifest/schema';
|
|
12
12
|
import { setupTestDatabaseAt as migrateDbFile } from '../test-utils/database';
|
|
13
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
13
14
|
import {
|
|
14
15
|
registerModuleSubscriptions,
|
|
15
16
|
resolveSubscription,
|
|
@@ -341,7 +342,7 @@ describe('resyncAllSubscriptions (ISS-0088)', () => {
|
|
|
341
342
|
});
|
|
342
343
|
afterEach(() => {
|
|
343
344
|
closeDb();
|
|
344
|
-
|
|
345
|
+
resetTestDbPath();
|
|
345
346
|
process.env.EVENT_BUS_DB = undefined;
|
|
346
347
|
try {
|
|
347
348
|
rmSync(dir, { recursive: true, force: true });
|
|
@@ -7,6 +7,7 @@ import { eq } from 'drizzle-orm';
|
|
|
7
7
|
import type { DbClient } from '../db/client';
|
|
8
8
|
import { portForwards } from '../db/schema';
|
|
9
9
|
import { setupTestDatabaseAt } from '../test-utils/database';
|
|
10
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
10
11
|
import { deleteClaimedRows } from './capability-table-rows';
|
|
11
12
|
import { buildPortForwardStore } from './port-forwards';
|
|
12
13
|
|
|
@@ -27,7 +28,7 @@ describe('port-forward store', () => {
|
|
|
27
28
|
});
|
|
28
29
|
afterEach(() => {
|
|
29
30
|
db.$client.close();
|
|
30
|
-
|
|
31
|
+
resetTestDbPath();
|
|
31
32
|
try {
|
|
32
33
|
rmSync(dir, { recursive: true, force: true });
|
|
33
34
|
} catch {
|
|
@@ -17,6 +17,7 @@ import { join } from 'node:path';
|
|
|
17
17
|
import { defineEvents, openBus } from '@celilo/event-bus';
|
|
18
18
|
import { closeDb, getDb } from '../db/client';
|
|
19
19
|
import { runMigrations } from '../db/migrate';
|
|
20
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
20
21
|
import { type AspectRequiredPayload, EVENT_TYPES } from './bus-interview';
|
|
21
22
|
import { startProgrammaticResponder } from './programmatic-responder';
|
|
22
23
|
|
|
@@ -72,7 +73,7 @@ describe('programmatic responder — aspect.required consent (#262)', () => {
|
|
|
72
73
|
afterEach(() => {
|
|
73
74
|
closeDb();
|
|
74
75
|
rmSync(dir, { recursive: true, force: true });
|
|
75
|
-
|
|
76
|
+
resetTestDbPath();
|
|
76
77
|
});
|
|
77
78
|
|
|
78
79
|
it('approves (consented=true) when the policy approves the module', async () => {
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
modules,
|
|
28
28
|
} from '../db/schema';
|
|
29
29
|
import type { BaseModuleAspect } from '../manifest/schema';
|
|
30
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
30
31
|
import { upsertModuleConfig } from './module-config';
|
|
31
32
|
import { executeProxmoxReconcile, planProxmoxReconcile } from './proxmox-reconcile';
|
|
32
33
|
|
|
@@ -153,7 +154,7 @@ describe('proxmox-reconcile', () => {
|
|
|
153
154
|
|
|
154
155
|
afterEach(() => {
|
|
155
156
|
closeDb();
|
|
156
|
-
|
|
157
|
+
resetTestDbPath();
|
|
157
158
|
try {
|
|
158
159
|
rmSync(dir, { recursive: true, force: true });
|
|
159
160
|
} catch {
|
|
@@ -23,6 +23,7 @@ import { tmpdir } from 'node:os';
|
|
|
23
23
|
import { join } from 'node:path';
|
|
24
24
|
import { closeDb } from '../db/client';
|
|
25
25
|
import { runMigrations } from '../db/migrate';
|
|
26
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
26
27
|
import { applyStagedSystemFiles, restoreFromArtifactFile } from './restore-from-file';
|
|
27
28
|
|
|
28
29
|
describe('applyStagedSystemFiles', () => {
|
|
@@ -44,7 +45,7 @@ describe('applyStagedSystemFiles', () => {
|
|
|
44
45
|
|
|
45
46
|
afterEach(() => {
|
|
46
47
|
closeDb();
|
|
47
|
-
|
|
48
|
+
resetTestDbPath();
|
|
48
49
|
process.env.CELILO_MASTER_KEY_PATH = undefined;
|
|
49
50
|
process.env.CELILO_DATA_DIR = undefined;
|
|
50
51
|
try {
|
|
@@ -205,7 +206,7 @@ describe('restoreFromArtifactFile error paths', () => {
|
|
|
205
206
|
|
|
206
207
|
afterEach(() => {
|
|
207
208
|
closeDb();
|
|
208
|
-
|
|
209
|
+
resetTestDbPath();
|
|
209
210
|
process.env.CELILO_DATA_DIR = undefined;
|
|
210
211
|
process.env.CELILO_MASTER_KEY_PATH = undefined;
|
|
211
212
|
try {
|
|
@@ -5,6 +5,7 @@ import { join } from 'node:path';
|
|
|
5
5
|
import { closeDb, getDb } from '../db/client';
|
|
6
6
|
import { runMigrations } from '../db/migrate';
|
|
7
7
|
import { modules } from '../db/schema';
|
|
8
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
8
9
|
import {
|
|
9
10
|
NonEmptyRestoreTargetError,
|
|
10
11
|
assertRestoreTargetEmpty,
|
|
@@ -23,7 +24,7 @@ describe('restore pre-flight', () => {
|
|
|
23
24
|
|
|
24
25
|
afterEach(() => {
|
|
25
26
|
closeDb();
|
|
26
|
-
|
|
27
|
+
resetTestDbPath();
|
|
27
28
|
process.env.CELILO_DATA_DIR = undefined;
|
|
28
29
|
try {
|
|
29
30
|
rmSync(dir, { recursive: true, force: true });
|
|
@@ -4,6 +4,7 @@ import { tmpdir } from 'node:os';
|
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { type DbClient, getDb } from '../db/client';
|
|
6
6
|
import { modules } from '../db/schema';
|
|
7
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
7
8
|
import {
|
|
8
9
|
type SecretsSchema,
|
|
9
10
|
getAllSecretMetadata,
|
|
@@ -40,7 +41,7 @@ describe('secret-schema-loader', () => {
|
|
|
40
41
|
afterEach(() => {
|
|
41
42
|
// Clean up temp directory
|
|
42
43
|
rmSync(tempDir, { recursive: true, force: true });
|
|
43
|
-
|
|
44
|
+
resetTestDbPath();
|
|
44
45
|
});
|
|
45
46
|
|
|
46
47
|
describe('loadSecretsSchema', () => {
|
|
@@ -8,6 +8,7 @@ import { tmpdir } from 'node:os';
|
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
import { closeDb } from '../db/client';
|
|
10
10
|
import { runMigrations } from '../db/migrate';
|
|
11
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
11
12
|
import { addMachine } from './machine-pool';
|
|
12
13
|
import {
|
|
13
14
|
LOCAL_MACHINE_IP,
|
|
@@ -97,7 +98,7 @@ describe('ssh-key-manager', () => {
|
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
// Clear environment variables
|
|
100
|
-
|
|
101
|
+
resetTestDbPath();
|
|
101
102
|
process.env.CELILO_DATA_DIR = undefined;
|
|
102
103
|
process.env.CELILO_MASTER_KEY_PATH = undefined;
|
|
103
104
|
});
|
|
@@ -14,6 +14,7 @@ import { eq } from 'drizzle-orm';
|
|
|
14
14
|
import type { DbClient } from '../db/client';
|
|
15
15
|
import { moduleConfigs, modules, webRoutes } from '../db/schema';
|
|
16
16
|
import { setupTestDatabaseAt } from '../test-utils/database';
|
|
17
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
17
18
|
import {
|
|
18
19
|
ansibleTreeMentions,
|
|
19
20
|
buildStaticContentPlan,
|
|
@@ -64,7 +65,7 @@ describe('planStaticContent — the declared release set (D10 task 4.1)', () =>
|
|
|
64
65
|
|
|
65
66
|
afterEach(() => {
|
|
66
67
|
db.$client.close();
|
|
67
|
-
|
|
68
|
+
resetTestDbPath();
|
|
68
69
|
try {
|
|
69
70
|
rmSync(dir, { recursive: true, force: true });
|
|
70
71
|
} catch {
|
|
@@ -177,7 +178,7 @@ describe('resolveStaticContentRetention — operator config with a default', ()
|
|
|
177
178
|
|
|
178
179
|
afterEach(() => {
|
|
179
180
|
db.$client.close();
|
|
180
|
-
|
|
181
|
+
resetTestDbPath();
|
|
181
182
|
try {
|
|
182
183
|
rmSync(dir, { recursive: true, force: true });
|
|
183
184
|
} catch {
|
|
@@ -279,7 +280,7 @@ describe('the state web overlay (celilo#1265)', () => {
|
|
|
279
280
|
|
|
280
281
|
afterEach(() => {
|
|
281
282
|
db.$client.close();
|
|
282
|
-
|
|
283
|
+
resetTestDbPath();
|
|
283
284
|
try {
|
|
284
285
|
rmSync(dir, { recursive: true, force: true });
|
|
285
286
|
} catch {
|
|
@@ -359,7 +360,7 @@ describe('writeStaticContentVars — lands in the generated inventory', () => {
|
|
|
359
360
|
|
|
360
361
|
afterEach(() => {
|
|
361
362
|
db.$client.close();
|
|
362
|
-
|
|
363
|
+
resetTestDbPath();
|
|
363
364
|
try {
|
|
364
365
|
rmSync(dir, { recursive: true, force: true });
|
|
365
366
|
} catch {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* `module verify` (celilo#1018, deliberately left to that thread).
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
26
|
+
import { existsSync, readFileSync, readdirSync, rmSync } from 'node:fs';
|
|
27
27
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
28
28
|
import { join } from 'node:path';
|
|
29
29
|
import { and, eq, isNotNull } from 'drizzle-orm';
|
|
@@ -237,9 +237,10 @@ export function staticContentVarsYaml(plan: StaticContentPlan): string {
|
|
|
237
237
|
*
|
|
238
238
|
* The shared half of both converge triggers: the capability's callback adds
|
|
239
239
|
* `executeAnsible` after this, and the provider's own deploy already runs it.
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
240
|
+
* Nothing inspects verbatim role assets any more — the staleness pre-flight
|
|
241
|
+
* retired with the ephemeral generated tree (D5 of
|
|
242
|
+
* control-plane-stops-building-modules), so an injected vars file cannot read
|
|
243
|
+
* as a stale generation because nothing reads staleness.
|
|
243
244
|
*/
|
|
244
245
|
export async function writeStaticContentVars(
|
|
245
246
|
db: DbClient,
|
|
@@ -308,13 +309,31 @@ export async function convergeStaticContent(
|
|
|
308
309
|
return { success: false, error: `Provider module '${providerModuleId}' is not installed` };
|
|
309
310
|
}
|
|
310
311
|
const generatedPath = join(module.sourcePath, 'generated');
|
|
312
|
+
// D4 (control-plane-stops-building-modules): the generated project is
|
|
313
|
+
// ephemeral, so a successful provider deploy deletes it and nothing is
|
|
314
|
+
// persisted for this converge to read. Render it here, on demand, from the
|
|
315
|
+
// signed payload plus resolved config — generation is fast and local. A
|
|
316
|
+
// render failure fails loudly with the deploy-once remedy, same as the old
|
|
317
|
+
// missing-project refusal did; a tree that predates the converge support is
|
|
318
|
+
// still refused by the static_releases check below.
|
|
311
319
|
if (!existsSync(generatedPath)) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
320
|
+
const { generateTemplates } = await import('../templates/generator');
|
|
321
|
+
const rendered = await generateTemplates({
|
|
322
|
+
moduleId: providerModuleId,
|
|
323
|
+
modulePath: module.sourcePath,
|
|
324
|
+
outputPath: generatedPath,
|
|
325
|
+
db,
|
|
326
|
+
skipVariableValidation: false,
|
|
327
|
+
});
|
|
328
|
+
if (!rendered.success) {
|
|
329
|
+
return {
|
|
330
|
+
success: false,
|
|
331
|
+
error:
|
|
332
|
+
`public_web provider '${providerModuleId}' has no generated deploy project at ${generatedPath} ` +
|
|
333
|
+
`and rendering one failed: ${rendered.error ?? 'unknown error'}. ` +
|
|
334
|
+
`Run \`celilo module deploy ${providerModuleId}\` once, then retry the publish.`,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
318
337
|
}
|
|
319
338
|
|
|
320
339
|
const written = await writeStaticContentVars(db, providerModuleId, generatedPath);
|
|
@@ -342,5 +361,9 @@ export async function convergeStaticContent(
|
|
|
342
361
|
varsPath: written.varsPath,
|
|
343
362
|
};
|
|
344
363
|
}
|
|
364
|
+
// D4: the converge rendered the tree, used it, and is done with it. The same
|
|
365
|
+
// no-secrets-at-rest rule the deploy follows applies here; on the failure
|
|
366
|
+
// paths above the tree stays for inspection.
|
|
367
|
+
rmSync(generatedPath, { recursive: true, force: true });
|
|
345
368
|
return { success: true, varsPath: written.varsPath };
|
|
346
369
|
}
|
|
@@ -13,6 +13,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
|
|
|
13
13
|
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
14
14
|
import { tmpdir } from 'node:os';
|
|
15
15
|
import { join } from 'node:path';
|
|
16
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
16
17
|
import { snapshotDatabase, stageSystemState } from './system-state-stage';
|
|
17
18
|
|
|
18
19
|
describe('stageSystemState', () => {
|
|
@@ -38,7 +39,7 @@ describe('stageSystemState', () => {
|
|
|
38
39
|
|
|
39
40
|
afterEach(() => {
|
|
40
41
|
process.env.CELILO_DATA_DIR = undefined;
|
|
41
|
-
|
|
42
|
+
resetTestDbPath();
|
|
42
43
|
process.env.CELILO_MASTER_KEY_PATH = undefined;
|
|
43
44
|
rmSync(dataDir, { recursive: true, force: true });
|
|
44
45
|
});
|
|
@@ -6,6 +6,7 @@ import type { DbClient } from '../db/client';
|
|
|
6
6
|
import { systemConfig } from '../db/schema';
|
|
7
7
|
import { loadTrustedSubnets } from '../hooks/capability-loader';
|
|
8
8
|
import { setupTestDatabaseAt } from '../test-utils/database';
|
|
9
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
9
10
|
import {
|
|
10
11
|
buildTrustedSourceStore,
|
|
11
12
|
composeTrustedSubnets,
|
|
@@ -30,7 +31,7 @@ describe('trusted-source store', () => {
|
|
|
30
31
|
});
|
|
31
32
|
afterEach(() => {
|
|
32
33
|
db.$client.close();
|
|
33
|
-
|
|
34
|
+
resetTestDbPath();
|
|
34
35
|
try {
|
|
35
36
|
rmSync(dir, { recursive: true, force: true });
|
|
36
37
|
} catch {
|
|
@@ -147,7 +148,7 @@ describe('the render input excludes registrations; the reporting view includes t
|
|
|
147
148
|
});
|
|
148
149
|
afterEach(() => {
|
|
149
150
|
db.$client.close();
|
|
150
|
-
|
|
151
|
+
resetTestDbPath();
|
|
151
152
|
try {
|
|
152
153
|
rmSync(dir, { recursive: true, force: true });
|
|
153
154
|
} catch {
|
|
@@ -9,6 +9,7 @@ import { join } from 'node:path';
|
|
|
9
9
|
import { closeDb, createDbClient } from '../db/client';
|
|
10
10
|
import { runMigrations } from '../db/migrate';
|
|
11
11
|
import { systemConfig } from '../db/schema';
|
|
12
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
12
13
|
import { detectZoneFromIp, isValidIp } from './zone-detector';
|
|
13
14
|
|
|
14
15
|
describe('zone-detector', () => {
|
|
@@ -46,7 +47,7 @@ describe('zone-detector', () => {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
// Clear environment variables
|
|
49
|
-
|
|
50
|
+
resetTestDbPath();
|
|
50
51
|
});
|
|
51
52
|
|
|
52
53
|
describe('detectZoneFromIp', () => {
|