@celilo/e2e 0.9.2 → 0.10.0

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.
Files changed (46) hide show
  1. package/config/cpanel-host/cpanel-host-ca.crt +20 -0
  2. package/config/cpanel-host/docroot/.htaccess +14 -0
  3. package/config/cpanel-host/docroot/cms.html +4 -0
  4. package/config/cpanel-host/site-tls.crt +20 -0
  5. package/config/cpanel-host/site-tls.key +28 -0
  6. package/config/cpanel-host/site.conf +27 -0
  7. package/config/dns/com.zone +7 -0
  8. package/config/dns/knot-namecheap.conf +6 -0
  9. package/config/dns/tangohost.com.zone +14 -0
  10. package/config/proxy/squid.conf +7 -2
  11. package/config/resolver/unbound.conf +39 -3
  12. package/config/routing/management-routes.sh +9 -4
  13. package/config/routing/resolver-internal-routes.sh +10 -0
  14. package/docker/Dockerfile.cpanel-host-sim +70 -0
  15. package/docker/Dockerfile.firewall +12 -0
  16. package/docker/Dockerfile.management +6 -0
  17. package/docker/Dockerfile.minio +1 -1
  18. package/docker/Dockerfile.observer +11 -0
  19. package/docker/Dockerfile.signal-cli +60 -0
  20. package/docker/Dockerfile.signal-release +36 -0
  21. package/docker/Dockerfile.signal-sim +17 -0
  22. package/docker/Dockerfile.target-machine +10 -1
  23. package/docker/Dockerfile.target-machine-docker +1 -1
  24. package/docker/Dockerfile.target-technitium +1 -1
  25. package/package.json +1 -1
  26. package/registry-server/src/auth.ts +2 -2
  27. package/registry-server/src/introspection.test.ts +247 -0
  28. package/registry-server/src/introspection.ts +204 -0
  29. package/registry-server/src/module-owner-store.test.ts +85 -0
  30. package/registry-server/src/module-owner-store.ts +108 -0
  31. package/registry-server/src/server.test.ts +262 -0
  32. package/registry-server/src/server.ts +139 -10
  33. package/scripts/pack-celilo-packages.ts +79 -51
  34. package/scripts/stage-libsignal.ts +116 -0
  35. package/simulators/signal-cli/server.ts +272 -0
  36. package/src/cli/build.ts +35 -9
  37. package/src/cli/command-registry.ts +34 -6
  38. package/src/cli/index.ts +59 -21
  39. package/src/container-manager.ts +6 -2
  40. package/src/docker-compose-generator.ts +108 -4
  41. package/src/network-builder.ts +52 -0
  42. package/src/runner.ts +128 -57
  43. package/src/simulator-ips.ts +9 -0
  44. package/src/types.ts +34 -0
  45. package/config/dns/example.net.zone +0 -16
  46. package/config/dns/iamtheinternet.org.zone +0 -16
package/src/runner.ts CHANGED
@@ -17,11 +17,11 @@
17
17
  * Countdown goes negative when over estimate.
18
18
  */
19
19
 
20
- import { spawn, spawnSync, execSync } from 'node:child_process';
20
+ import { execSync, spawn, spawnSync } from 'node:child_process';
21
21
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
22
22
  import { basename, dirname, join, resolve } from 'node:path';
23
- import { parse as parseYaml } from 'yaml';
24
23
  import { type DisplayMode, ProgressDisplay } from '@celilo/cli-display';
24
+ import { parse as parseYaml } from 'yaml';
25
25
  import {
26
26
  emitRunCompleted,
27
27
  emitRunFailed,
@@ -30,7 +30,7 @@ import {
30
30
  emitTestStarted,
31
31
  } from './bus-events';
32
32
  import { parseLine } from './parse-line';
33
- import { acquireRunLock, E2eBusyError, markKept } from './run-lock';
33
+ import { E2eBusyError, acquireRunLock, markKept } from './run-lock';
34
34
  import { SIMULATOR_IPS } from './simulator-ips';
35
35
 
36
36
  // ─── Config ──────────────────────────────────────────────────────────
@@ -50,7 +50,9 @@ function resolveModuleTestsDir(dir: string): string {
50
50
  const manifestPath = join(dir, 'manifest.yml');
51
51
  if (existsSync(manifestPath)) {
52
52
  try {
53
- const parsed = parseYaml(readFileSync(manifestPath, 'utf-8')) as { e2e?: { tests_dir?: string } };
53
+ const parsed = parseYaml(readFileSync(manifestPath, 'utf-8')) as {
54
+ e2e?: { tests_dir?: string };
55
+ };
54
56
  if (parsed?.e2e?.tests_dir) return resolve(dir, parsed.e2e.tests_dir);
55
57
  } catch {}
56
58
  }
@@ -84,11 +86,14 @@ const flagCi = rawArgs.includes('--ci');
84
86
  const flagNoCi = rawArgs.includes('--no-ci');
85
87
  const ci = flagCi || (!flagNoCi && !!process.env.CI);
86
88
  const patterns = rawArgs.filter((a) => !a.startsWith('--'));
87
- // --ci-safe: skip tests marked `cele2e-ci-unsafe` in their file (quarantined —
88
- // known-failing or builder-flaky, each tracked by an ISS). `--complete`/`--all`
89
- // run everything. Passed through from the CLI (not stripped) so the runner can
90
- // apply the exclusion.
91
- const flagCiSafe = rawArgs.includes('--ci-safe');
89
+ // Quarantine: tests whose file contains the marker `cele2e-ci-unsafe` (a comment
90
+ // naming the reason + ISS) are known-failing / builder-flaky and tracked
91
+ // separately, so they must never turn a broad run red. They are SKIPPED by
92
+ // default in every broad run (`--all`, `--all-modules`, `--ci-safe`, bare). Two
93
+ // ways to run one anyway: name it explicitly (`cele2e run <name>`), or pass
94
+ // `--complete` (the deliberate "run everything, including quarantined" opt-in).
95
+ // `--complete` is passed through from the CLI (not stripped) so the runner sees it.
96
+ const flagComplete = rawArgs.includes('--complete');
92
97
  // --notify: fire a desktop notification when the run finishes (handy for long
93
98
  // detached/background runs). Best-effort — never affects the exit code.
94
99
  const flagNotify = rawArgs.includes('--notify');
@@ -123,9 +128,20 @@ function notify(title: string, message: string): void {
123
128
  if (!flagNotify) return;
124
129
  try {
125
130
  if (process.platform === 'darwin') {
126
- const r = spawnSync('terminal-notifier', ['-title', title, '-message', message, '-sound', 'Glass'], { stdio: 'ignore' });
131
+ const r = spawnSync(
132
+ 'terminal-notifier',
133
+ ['-title', title, '-message', message, '-sound', 'Glass'],
134
+ { stdio: 'ignore' },
135
+ );
127
136
  if (r.error) {
128
- spawnSync('osascript', ['-e', `display notification ${JSON.stringify(message)} with title ${JSON.stringify(title)} sound name "Glass"`], { stdio: 'ignore' });
137
+ spawnSync(
138
+ 'osascript',
139
+ [
140
+ '-e',
141
+ `display notification ${JSON.stringify(message)} with title ${JSON.stringify(title)} sound name "Glass"`,
142
+ ],
143
+ { stdio: 'ignore' },
144
+ );
129
145
  }
130
146
  spawnSync('say', [message], { stdio: 'ignore' });
131
147
  } else if (process.platform === 'linux') {
@@ -182,7 +198,10 @@ function extractFailureMessage(lines: string[], exitCode: number): string {
182
198
  // bun's own test failure: "✗ test name [Xms]" — extract the failing test name
183
199
  const bunFailIdx = plain.findIndex((l) => /^\s*✗\s+\S/.test(l) && !l.includes('failed ('));
184
200
  if (bunFailIdx >= 0) {
185
- const testName = plain[bunFailIdx].replace(/^\s*✗\s+/, '').replace(/\s*\[\d+ms\].*$/, '').trim();
201
+ const testName = plain[bunFailIdx]
202
+ .replace(/^\s*✗\s+/, '')
203
+ .replace(/\s*\[\d+ms\].*$/, '')
204
+ .trim();
186
205
  // Grab the error detail on the next few lines (bun indents it under the test)
187
206
  const detail = plain
188
207
  .slice(bunFailIdx + 1, bunFailIdx + 6)
@@ -215,7 +234,13 @@ function extractFailureMessage(lines: string[], exitCode: number): string {
215
234
 
216
235
  // Last resort: print the last few non-empty, non-progress lines from the output
217
236
  const tail = plain
218
- .filter((l) => l.length > 0 && !l.startsWith('[progress') && !l.startsWith('[e2e:') && !l.startsWith('[ansible:'))
237
+ .filter(
238
+ (l) =>
239
+ l.length > 0 &&
240
+ !l.startsWith('[progress') &&
241
+ !l.startsWith('[e2e:') &&
242
+ !l.startsWith('[ansible:'),
243
+ )
219
244
  .slice(-3)
220
245
  .map((l) => l.trim())
221
246
  .join(' | ');
@@ -270,7 +295,9 @@ async function runTest(
270
295
  testEnv.CELILO_E2E_REUSE = '1';
271
296
  testEnv.CELILO_E2E_PROJECT = data.projectName;
272
297
  } catch {
273
- console.error(`${red}✗ --reuse: no persistent network found. Run with --keep first.${reset}`);
298
+ console.error(
299
+ `${red}✗ --reuse: no persistent network found. Run with --keep first.${reset}`,
300
+ );
274
301
  process.exit(1);
275
302
  }
276
303
  }
@@ -298,23 +325,32 @@ async function runTest(
298
325
  process.stdout.write(`${bold}\x1b[33m║${reset} Container: ${bold}${container}${reset}\n`);
299
326
  process.stdout.write(`${bold}\x1b[33m║${reset}\n`);
300
327
  process.stdout.write(`${bold}\x1b[33m║${reset} ${dim}Run commands:${reset}\n`);
301
- process.stdout.write(`${bold}\x1b[33m║${reset} docker compose -f docker-compose.test.yml \\\n`);
302
- process.stdout.write(`${bold}\x1b[33m║${reset} -p ${project} exec ${container} bash -c '<cmd>'\n`);
328
+ process.stdout.write(
329
+ `${bold}\x1b[33m║${reset} docker compose -f docker-compose.test.yml \\\n`,
330
+ );
331
+ process.stdout.write(
332
+ `${bold}\x1b[33m║${reset} -p ${project} exec ${container} bash -c '<cmd>'\n`,
333
+ );
303
334
  process.stdout.write(`${bold}\x1b[33m║${reset}\n`);
304
335
 
305
336
  if (interactive) {
306
- process.stdout.write(`${bold}\x1b[33m║${reset} ${dim}Or get a shell: type 'exit' to resume the test.${reset}\n`);
337
+ process.stdout.write(
338
+ `${bold}\x1b[33m║${reset} ${dim}Or get a shell: type 'exit' to resume the test.${reset}\n`,
339
+ );
307
340
  process.stdout.write(`${bold}\x1b[33m╚${border}${reset}\n\n`);
308
341
 
309
- spawnSync('docker', [
310
- 'compose', '-f', 'docker-compose.test.yml', '-p', project,
311
- 'exec', container, 'bash',
312
- ], {
313
- cwd: PKG_DIR,
314
- stdio: 'inherit',
315
- });
342
+ spawnSync(
343
+ 'docker',
344
+ ['compose', '-f', 'docker-compose.test.yml', '-p', project, 'exec', container, 'bash'],
345
+ {
346
+ cwd: PKG_DIR,
347
+ stdio: 'inherit',
348
+ },
349
+ );
316
350
  } else {
317
- process.stdout.write(`${bold}\x1b[33m║${reset} ${dim}Resume: touch ${signalFile}${reset}\n`);
351
+ process.stdout.write(
352
+ `${bold}\x1b[33m║${reset} ${dim}Resume: touch ${signalFile}${reset}\n`,
353
+ );
318
354
  process.stdout.write(`${bold}\x1b[33m╚${border}${reset}\n\n`);
319
355
 
320
356
  // Non-interactive: just wait for the signal file (no shell)
@@ -376,7 +412,7 @@ async function runTest(
376
412
  writeFileSync(logFile, stdout + '\n---STDERR---\n' + stderr);
377
413
 
378
414
  const expectMatch = stdout.match(/(\d+) expect\(\) calls/);
379
- const expectCount = expectMatch ? parseInt(expectMatch[1], 10) : undefined;
415
+ const expectCount = expectMatch ? Number.parseInt(expectMatch[1], 10) : undefined;
380
416
 
381
417
  if (code === 0) {
382
418
  if (expectCount === 0) {
@@ -389,7 +425,13 @@ async function runTest(
389
425
  error: '0 assertions — test likely exited before reaching assertions',
390
426
  });
391
427
  } else {
392
- resolveTest({ name, status: 'pass', duration, hadDebugPause, projectName: capturedProjectName });
428
+ resolveTest({
429
+ name,
430
+ status: 'pass',
431
+ duration,
432
+ hadDebugPause,
433
+ projectName: capturedProjectName,
434
+ });
393
435
  }
394
436
  } else {
395
437
  // Collect a raw tail: strip ANSI, drop internal protocol lines, keep last 30
@@ -397,12 +439,13 @@ async function runTest(
397
439
  .split('\n')
398
440
  .map(stripAnsi)
399
441
  .map((l) => l.trimEnd())
400
- .filter((l) =>
401
- l.length > 0 &&
402
- !l.startsWith('[progress') &&
403
- !l.startsWith('[e2e:') &&
404
- !l.startsWith('[ansible:') &&
405
- !l.startsWith('[debug:'),
442
+ .filter(
443
+ (l) =>
444
+ l.length > 0 &&
445
+ !l.startsWith('[progress') &&
446
+ !l.startsWith('[e2e:') &&
447
+ !l.startsWith('[ansible:') &&
448
+ !l.startsWith('[debug:'),
406
449
  )
407
450
  .slice(-30)
408
451
  .join('\n');
@@ -424,10 +467,12 @@ async function runTest(
424
467
 
425
468
  function dockerComposeShared(cmd: string): string {
426
469
  try {
427
- return execSync(
428
- `docker compose -f docker-compose.shared.yml -p celilo-e2e-shared ${cmd}`,
429
- { cwd: PKG_DIR, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 60_000 },
430
- ).trim();
470
+ return execSync(`docker compose -f docker-compose.shared.yml -p celilo-e2e-shared ${cmd}`, {
471
+ cwd: PKG_DIR,
472
+ encoding: 'utf-8',
473
+ stdio: ['pipe', 'pipe', 'pipe'],
474
+ timeout: 60_000,
475
+ }).trim();
431
476
  } catch {
432
477
  return '';
433
478
  }
@@ -435,7 +480,9 @@ function dockerComposeShared(cmd: string): string {
435
480
 
436
481
  function stopSharedInfra(): void {
437
482
  dockerComposeShared('down --volumes --remove-orphans');
438
- try { execSync('docker network prune -f', { stdio: 'pipe', timeout: 10_000 }); } catch {}
483
+ try {
484
+ execSync('docker network prune -f', { stdio: 'pipe', timeout: 10_000 });
485
+ } catch {}
439
486
  }
440
487
 
441
488
  /**
@@ -486,14 +533,20 @@ async function main() {
486
533
  // docker mutation so a competing run can't wipe our shared infra mid-setup.
487
534
  const lockLabel = patterns.length ? patterns.join(',') : moduleDirs.length ? 'modules' : 'all';
488
535
  try {
489
- acquireRunLock({ test: lockLabel, runId: process.env.CELE2E_RUN_ID ?? ambientRunId, allowKept: flagReuse });
536
+ acquireRunLock({
537
+ test: lockLabel,
538
+ runId: process.env.CELE2E_RUN_ID ?? ambientRunId,
539
+ allowKept: flagReuse,
540
+ });
490
541
  // A --reuse run keeps using a kept stack that stays up afterwards — hold the
491
542
  // lock in kept state so it isn't freed out from under the reused network.
492
543
  if (flagReuse) markKept();
493
544
  } catch (err) {
494
545
  if (err instanceof E2eBusyError) {
495
546
  console.error(`\n${red}${err.message}${reset}`);
496
- console.error(`${dim}Poll with: cele2e status (machine-readable: cele2e status --json)${reset}\n`);
547
+ console.error(
548
+ `${dim}Poll with: cele2e status (machine-readable: cele2e status --json)${reset}\n`,
549
+ );
497
550
  process.exit(3);
498
551
  }
499
552
  throw err;
@@ -511,7 +564,9 @@ async function main() {
511
564
  const tp = resolveModuleTestsDir(dir);
512
565
  if (!existsSync(tp)) {
513
566
  console.error(`${red}No tests directory found at ${tp}${reset}`);
514
- console.error(`${red}Set e2e.tests_dir in ${join(dir, 'manifest.yml')} to point to your test files.${reset}`);
567
+ console.error(
568
+ `${red}Set e2e.tests_dir in ${join(dir, 'manifest.yml')} to point to your test files.${reset}`,
569
+ );
515
570
  process.exit(1);
516
571
  }
517
572
  // Auto-install e2e/ dependencies if package.json exists but node_modules is missing
@@ -573,11 +628,10 @@ async function main() {
573
628
  testFiles = testFiles.filter((f) => patterns.some((p) => basename(f, '.test.ts').includes(p)));
574
629
  }
575
630
 
576
- // --ci-safe: drop quarantined tests — those whose file contains the marker
577
- // `cele2e-ci-unsafe` (a comment naming the reason + ISS). Keeps the nightly
578
- // green on the CI-safe subset while the quarantined tests are tracked + fixed
579
- // separately. `--complete`/`--all` run everything (no exclusion).
580
- if (flagCiSafe) {
631
+ // Drop quarantined tests (file contains `cele2e-ci-unsafe`) from broad runs so
632
+ // they never turn a full run red. Skipped by default; bypassed only when the
633
+ // user explicitly named tests (`patterns.length > 0`) or passed `--complete`.
634
+ if (!flagComplete && patterns.length === 0) {
581
635
  const before = testFiles.length;
582
636
  testFiles = testFiles.filter((f) => {
583
637
  try {
@@ -588,7 +642,9 @@ async function main() {
588
642
  });
589
643
  const skipped = before - testFiles.length;
590
644
  if (skipped > 0) {
591
- console.log(`${dim}--ci-safe: skipped ${skipped} quarantined (cele2e-ci-unsafe) test(s)${reset}`);
645
+ console.log(
646
+ `${dim}skipped ${skipped} quarantined (cele2e-ci-unsafe) test(s) — pass --complete or name one to run it${reset}`,
647
+ );
592
648
  }
593
649
  }
594
650
 
@@ -637,7 +693,9 @@ async function main() {
637
693
  console.log(`${bold}╔══════════════════════════════════════════════════════════════${reset}`);
638
694
  console.log(`${bold}║ Celilo E2E Tests${moduleLabel}${reset}`);
639
695
  if (hasHistory) {
640
- console.log(`${bold}║ ${testFiles.length} test(s) — expected ~${formatDuration(suiteExpected)}${reset}`);
696
+ console.log(
697
+ `${bold}║ ${testFiles.length} test(s) — expected ~${formatDuration(suiteExpected)}${reset}`,
698
+ );
641
699
  } else {
642
700
  console.log(`${bold}║ ${testFiles.length} test(s) — first run, no timing history${reset}`);
643
701
  }
@@ -741,7 +799,8 @@ async function main() {
741
799
  emitTestCompleted({
742
800
  runId,
743
801
  name,
744
- status: result.status === 'pass' ? 'pass' : result.status === 'suspicious' ? 'suspicious' : 'fail',
802
+ status:
803
+ result.status === 'pass' ? 'pass' : result.status === 'suspicious' ? 'suspicious' : 'fail',
745
804
  durationMs: result.duration * 1000,
746
805
  error: result.error,
747
806
  logDir,
@@ -757,11 +816,15 @@ async function main() {
757
816
  if (flagKeep && result.projectName) {
758
817
  writeFileSync(
759
818
  persistentFile,
760
- JSON.stringify({
761
- projectName: result.projectName,
762
- testFile: file,
763
- createdAt: new Date().toISOString(),
764
- }, null, 2) + '\n',
819
+ JSON.stringify(
820
+ {
821
+ projectName: result.projectName,
822
+ testFile: file,
823
+ createdAt: new Date().toISOString(),
824
+ },
825
+ null,
826
+ 2,
827
+ ) + '\n',
765
828
  );
766
829
  console.log(`${dim} Persistent network: ${result.projectName}${reset}`);
767
830
  console.log(`${dim} Reuse: ${basename(process.argv[1])} --reuse${reset}`);
@@ -800,7 +863,9 @@ async function main() {
800
863
  JSON.stringify({ status: result.status, duration: result.duration, error: result.error }),
801
864
  );
802
865
 
803
- try { execSync('docker network prune -f', { stdio: 'pipe', timeout: 10_000 }); } catch {}
866
+ try {
867
+ execSync('docker network prune -f', { stdio: 'pipe', timeout: 10_000 });
868
+ } catch {}
804
869
  }
805
870
 
806
871
  saveTiming(history);
@@ -820,7 +885,9 @@ async function main() {
820
885
  for (const r of results) {
821
886
  const icon = r.status === 'pass' ? '✓' : '✗';
822
887
  const color = r.status === 'pass' ? green : red;
823
- console.log(`${bold}║${reset} ${color}${icon}${reset} ${r.name.padEnd(28)} ${dim}${(r.duration + 's').padStart(6)}${reset}`);
888
+ console.log(
889
+ `${bold}║${reset} ${color}${icon}${reset} ${r.name.padEnd(28)} ${dim}${(r.duration + 's').padStart(6)}${reset}`,
890
+ );
824
891
  if (r.error) {
825
892
  console.log(`${bold}║${reset} ${red}└─ ${r.error}${reset}`);
826
893
  }
@@ -832,7 +899,11 @@ async function main() {
832
899
 
833
900
  writeFileSync(
834
901
  join(resultsDir, 'summary.json'),
835
- JSON.stringify({ timestamp, total: testFiles.length, passed, failed, duration: suiteDuration }, null, 2),
902
+ JSON.stringify(
903
+ { timestamp, total: testFiles.length, passed, failed, duration: suiteDuration },
904
+ null,
905
+ 2,
906
+ ),
836
907
  );
837
908
 
838
909
  const gitignore = join(testDir, '.gitignore');
@@ -35,6 +35,15 @@ export const SIMULATOR_IPS = {
35
35
  APT_REPO: '100.64.0.60',
36
36
  /** MinIO S3-compatible object storage — backup/restore target (replaces AWS S3 in tests). Reachable at http://minio.lab. */
37
37
  MINIO: '100.64.0.61',
38
+ /**
39
+ * OFF-FLEET cPanel/SSH web host simulator (`tangohost.com`) — a third-party
40
+ * host celilo publishes to via `external_web` but does NOT govern. A public
41
+ * peer of namecheap-dns/pebble, NOT behind the customer firewall, so public
42
+ * DNS pointing the domain here is a genuine public endpoint.
43
+ */
44
+ CPANEL_HOST: '100.64.0.63',
45
+ /** signal-cli release host — serves the tarball the signal module downloads at deploy time. */
46
+ SIGNAL_RELEASE: '100.64.0.62',
38
47
  /** Pebble ACME server (replaces production Let's Encrypt). */
39
48
  PEBBLE: '100.64.0.100',
40
49
  } as const;
package/src/types.ts CHANGED
@@ -45,6 +45,40 @@ export interface NetworkConfig {
45
45
  managementVolumes: string[];
46
46
  /** Include a DHCP client container on the internal network */
47
47
  dhcpClient: boolean;
48
+ /**
49
+ * Include a REAL signal-cli daemon on the internal network.
50
+ *
51
+ * Never linked to a Signal account — the JSON-RPC interface is served
52
+ * locally and needs none. It exists so the contract celilo's client depends
53
+ * on is verified against the actual binary rather than against a simulator
54
+ * that encodes the same assumptions. See design.md D16.
55
+ */
56
+ signalCli: boolean;
57
+ /**
58
+ * Include the signal-cli SIMULATOR on the internal network.
59
+ *
60
+ * The drivable counterpart to `signalCli`: it can queue an inbound reply,
61
+ * fail a recipient inside a JSON-RPC success, and revoke the device link on
62
+ * command, which is what a test of the delivery/ack loop needs and what an
63
+ * unlinked real daemon cannot be made to do.
64
+ */
65
+ signalSim: boolean;
66
+ /**
67
+ * Serve the signal-cli release tarball from the simulated internet, so the
68
+ * signal module's deploy-time download resolves inside the sealed network.
69
+ */
70
+ signalRelease: boolean;
71
+ /**
72
+ * Which zone the celilo management container sits in. Defaults to `internal`
73
+ * — the single-network topology every existing test uses.
74
+ *
75
+ * `secure-mgmt` places celilo-mgr on its OWN control-plane network, which is
76
+ * the production topology and the one the suite could not express before. That
77
+ * gap is why the control-plane-network bug reached production: with management
78
+ * always on `internal`, the hardcoded "trusted subnet == network.internal.subnet"
79
+ * assumption was accidentally true in every test.
80
+ */
81
+ managementZone?: 'internal' | 'secure-mgmt';
48
82
  /**
49
83
  * Observer vantages to inject into the topology. Each becomes an `observer-*`
50
84
  * container — a passive spy carrying the probe tooling, placed at the named
@@ -1,16 +0,0 @@
1
- $ORIGIN example.net.
2
- $TTL 300
3
-
4
- @ IN SOA ns1.example.net. admin.example.net. (
5
- 2024020108 300 60 604800 300
6
- )
7
-
8
- ; Public DNS — RFC 1918 addresses NEVER appear here. The whole point of
9
- ; the e2e simulation is to model real internet connectivity, where
10
- ; private IPs are unreachable from outside. All public-facing names
11
- ; resolve to the firewall's external IP (100.100.0.100), which DNATs
12
- ; inbound to the right internal host.
13
- @ IN NS ns1.example.net.
14
- ns1 IN A 100.64.0.55
15
- @ IN A 100.100.0.100
16
- www IN A 100.100.0.100
@@ -1,16 +0,0 @@
1
- $ORIGIN iamtheinternet.org.
2
- $TTL 300
3
-
4
- @ IN SOA ns1.iamtheinternet.org. admin.iamtheinternet.org. (
5
- 2024020247 300 60 604800 300
6
- )
7
-
8
- ; Public DNS — RFC 1918 addresses NEVER appear here. The whole point of
9
- ; the e2e simulation is to model real internet connectivity, where
10
- ; private IPs are unreachable from outside. All public-facing names
11
- ; resolve to the firewall's external IP (100.100.0.100), which DNATs
12
- ; inbound to the right internal host.
13
- @ IN NS ns1.iamtheinternet.org.
14
- ns1 IN A 100.64.0.55
15
- @ IN A 0.0.0.0
16
- www IN A 100.100.0.100