@celilo/e2e 0.19.0 → 0.19.2
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 +3 -3
- package/registry-server/src/bootstrap.ts +5 -0
- package/registry-server/src/server.test.ts +221 -1
- package/registry-server/src/server.ts +92 -0
- package/registry-server/src/storage.ts +95 -2
- package/registry-server/src/sweep.test.ts +326 -0
- package/registry-server/src/sweep.ts +289 -0
- package/scripts/consumer-paths-changed.ts +3 -0
- package/src/address-plan.test.ts +21 -1
- package/src/container-manager.ts +29 -15
- package/src/docker-compose-generator.ts +66 -22
- package/src/shared-infra.ts +6 -0
package/src/container-manager.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ExecSyncOptions, execSync, spawn } from 'node:child_process';
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { basename, join, resolve } from 'node:path';
|
|
5
5
|
import { parse as parseYaml } from 'yaml';
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
firewallZoneLegs,
|
|
11
11
|
generateTestComposeYaml,
|
|
12
12
|
getAllMachines,
|
|
13
|
+
registryUploadsHostDir,
|
|
13
14
|
} from './docker-compose-generator';
|
|
14
15
|
import { explainBuildFailure } from './doctor';
|
|
15
16
|
import { type ModuleHost, parseModuleHost } from './module-host';
|
|
@@ -613,20 +614,26 @@ function buildNetworkHandle(
|
|
|
613
614
|
);
|
|
614
615
|
// Give the box its fleet key BEFORE deploying, using the harness keypair.
|
|
615
616
|
//
|
|
616
|
-
// `ensureFleetKey` (
|
|
617
|
-
// `celilo-fleet`
|
|
618
|
-
//
|
|
619
|
-
// rig it is not survivable: every target
|
|
620
|
-
// baked into authorized_keys at boot,
|
|
621
|
-
// by nothing. celilo would hold a key
|
|
622
|
-
// `machine add` fails with "no matching
|
|
623
|
-
// — which reads as a missing fixture and
|
|
617
|
+
// `ensureFleetKey` (apps/celilo/src/services/fleet-key.ts, reached by the
|
|
618
|
+
// deploy through `celilo system ensure-fleet-key`) mints a new
|
|
619
|
+
// `celilo-fleet` key when none exists and records `ssh.public_key`. In a
|
|
620
|
+
// real fleet that is right. In the rig it is not survivable: every target
|
|
621
|
+
// container trusts the HARNESS key, baked into authorized_keys at boot,
|
|
622
|
+
// and a freshly minted key is trusted by nothing. celilo would hold a key
|
|
623
|
+
// no machine accepts, and the next `machine add` fails with "no matching
|
|
624
|
+
// private key was found in ~/.ssh/" — which reads as a missing fixture and
|
|
625
|
+
// is actually a rotated key.
|
|
624
626
|
//
|
|
625
627
|
// `ensureFleetKey` is idempotent and REUSES an existing key, so seeding it
|
|
626
628
|
// with the harness pair means the deploy runs its real code path and
|
|
627
629
|
// arrives at the key the fleet already trusts. This models an operator
|
|
628
630
|
// whose management box already has its fleet key, which is the ordinary
|
|
629
631
|
// case after the first deploy.
|
|
632
|
+
//
|
|
633
|
+
// The directory below must match `getFleetSshDir()`, which is
|
|
634
|
+
// `dirname(getDbPath())/.ssh`. The `db_path` set just above is what makes
|
|
635
|
+
// the two agree — the framework reads it from the env `on_install`
|
|
636
|
+
// exports before shelling out.
|
|
630
637
|
const stateSshDir = '/root/.local/share/celilo/.ssh';
|
|
631
638
|
dockerExec(
|
|
632
639
|
projectName,
|
|
@@ -1005,12 +1012,19 @@ function buildNetworkHandle(
|
|
|
1005
1012
|
}
|
|
1006
1013
|
|
|
1007
1014
|
try {
|
|
1008
|
-
//
|
|
1009
|
-
//
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1015
|
+
// Write HOST-side into the dir bound at the registry's /uploads. The
|
|
1016
|
+
// registry rescans that dir on every request, so it needs no HTTP
|
|
1017
|
+
// handshake and no restart.
|
|
1018
|
+
//
|
|
1019
|
+
// Deliberately not a `docker compose cp` into the container: /uploads
|
|
1020
|
+
// is a READ-ONLY bind in a consumer install, so the container write
|
|
1021
|
+
// failed for every npm-installed consumer while passing forever in the
|
|
1022
|
+
// monorepo, where the same path is not a mount at all (celilo#1142).
|
|
1023
|
+
// registryUploadsHostDir() is the same function the compose generator
|
|
1024
|
+
// builds the mount from, so the two cannot drift apart again.
|
|
1025
|
+
const uploadsDir = registryUploadsHostDir();
|
|
1026
|
+
mkdirSync(uploadsDir, { recursive: true });
|
|
1027
|
+
copyFileSync(netappPath, join(uploadsDir, `${moduleId}.netapp`));
|
|
1014
1028
|
} finally {
|
|
1015
1029
|
if (cleanup)
|
|
1016
1030
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, rmSync } from 'node:fs';
|
|
2
|
+
import { basename, join } from 'node:path';
|
|
3
3
|
import { stringify } from 'yaml';
|
|
4
4
|
import { normalizeObservers, observerEnv, observerPlacement } from './observer';
|
|
5
5
|
import { PROXMOX_SIM_IP, SIMULATOR_IPS } from './simulator-ips';
|
|
@@ -13,32 +13,76 @@ import {
|
|
|
13
13
|
zoneIp,
|
|
14
14
|
} from './types';
|
|
15
15
|
|
|
16
|
+
const PACKAGE_DIR = join(import.meta.dir, '..');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Is this @celilo/e2e running from the monorepo, or from an npm install?
|
|
20
|
+
*
|
|
21
|
+
* `<pkgDir>/../../modules` is `<repo>/modules` in the monorepo and
|
|
22
|
+
* `<consumer>/node_modules/modules` (nonexistent) in a consumer install.
|
|
23
|
+
*/
|
|
24
|
+
function isMonorepoInstall(): boolean {
|
|
25
|
+
return existsSync(join(PACKAGE_DIR, '..', '..', 'modules'));
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
29
|
+
* The host dir bound read-only at the registry's `/uploads` — and therefore
|
|
30
|
+
* the dir `publishModule` writes a `.netapp` into. The registry rescans it on
|
|
31
|
+
* every request, so a host-side write is picked up with no container write and
|
|
32
|
+
* no HTTP handshake.
|
|
33
|
+
*
|
|
34
|
+
* Both install shapes bind one. They differ only in WHICH, because they differ
|
|
35
|
+
* in where standard modules come from:
|
|
36
|
+
*
|
|
37
|
+
* - **Monorepo dev** — modules are served live from `/modules`
|
|
38
|
+
* (`../../modules`, BOOTSTRAP_MODULES_DIR mode), so edits to a module's
|
|
39
|
+
* source flow through without a manual repack. `/uploads` is therefore a
|
|
40
|
+
* dedicated drop-zone, emptied on every shared-infra start (see
|
|
41
|
+
* `prepareRegistryDropZone`) — a leftover `.netapp` would shadow live source.
|
|
19
42
|
*
|
|
20
|
-
* - **
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
43
|
+
* - **npm-installed consumer** (lunacycle and friends) — `../../modules` does
|
|
44
|
+
* not exist, so there is no source to serve. `/uploads` IS the netapp cache
|
|
45
|
+
* `cele2e build-infra` fills from the public registry (see
|
|
46
|
+
* stageNetappsFromRegistry in cli/build.ts), and a published module lands
|
|
47
|
+
* beside the standard ones. Consumers vendor nothing.
|
|
24
48
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
|
|
49
|
+
* Read this through `registryUploadsHostDir()` rather than re-deriving it.
|
|
50
|
+
* publishModule used to `docker cp` into the container's `/uploads` instead,
|
|
51
|
+
* which works only where that path is NOT a mount — so it passed forever in
|
|
52
|
+
* the monorepo and failed for every consumer against the read-only bind
|
|
53
|
+
* (celilo#1142). One function now decides the mount and the write target
|
|
54
|
+
* together, so they cannot disagree again.
|
|
55
|
+
*/
|
|
56
|
+
export function registryUploadsHostDir(): string {
|
|
57
|
+
return join(PACKAGE_DIR, isMonorepoInstall() ? 'uploads' : 'netapps');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Make the drop-zone ready for a shared-infra bring-up.
|
|
33
62
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
63
|
+
* Creates it: Docker silently creates a MISSING bind source as an empty
|
|
64
|
+
* root-owned directory, which the host then cannot write into.
|
|
65
|
+
*
|
|
66
|
+
* Empties it, but only in the monorepo, where `/uploads` is dedicated — a
|
|
67
|
+
* `.netapp` left by a previous run would shadow the live module source it was
|
|
68
|
+
* packed from. In a consumer install the same dir IS the build-infra netapp
|
|
69
|
+
* cache, so clearing it would delete every standard module. (Before
|
|
70
|
+
* celilo#1142 published netapps lived in the registry's container layer and
|
|
71
|
+
* died with the container, so starting empty is the behaviour being kept, not
|
|
72
|
+
* a new one.)
|
|
38
73
|
*/
|
|
74
|
+
export function prepareRegistryDropZone(): void {
|
|
75
|
+
const dir = registryUploadsHostDir();
|
|
76
|
+
mkdirSync(dir, { recursive: true });
|
|
77
|
+
if (!isMonorepoInstall()) return;
|
|
78
|
+
for (const file of readdirSync(dir)) {
|
|
79
|
+
if (file.endsWith('.netapp')) rmSync(join(dir, file), { force: true });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
39
83
|
function getRegistryVolumes(): string[] {
|
|
40
|
-
const
|
|
41
|
-
return
|
|
84
|
+
const uploads = `./${basename(registryUploadsHostDir())}:/uploads:ro`;
|
|
85
|
+
return isMonorepoInstall() ? ['../../modules:/modules:ro', uploads] : [uploads];
|
|
42
86
|
}
|
|
43
87
|
|
|
44
88
|
const NETWORK_DRIVER_OPTS = {
|
package/src/shared-infra.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
SHARED_NETWORKS,
|
|
18
18
|
SHARED_PROJECT_NAME,
|
|
19
19
|
generateSharedInfraYaml,
|
|
20
|
+
prepareRegistryDropZone,
|
|
20
21
|
} from './docker-compose-generator';
|
|
21
22
|
import { ensureRegistryServerBundle, ensureTerraformFakeBundle } from './registry-bundle';
|
|
22
23
|
|
|
@@ -161,6 +162,11 @@ export async function ensureSharedInfra(): Promise<void> {
|
|
|
161
162
|
const yaml = generateSharedInfraYaml();
|
|
162
163
|
writeFileSync(join(e2eDir, SHARED_COMPOSE_FILE), yaml);
|
|
163
164
|
|
|
165
|
+
// The registry's /uploads bind source must exist before `up` (a missing
|
|
166
|
+
// bind source becomes an empty root-owned dir), and in the monorepo it
|
|
167
|
+
// starts empty so no stale .netapp shadows live module source.
|
|
168
|
+
prepareRegistryDropZone();
|
|
169
|
+
|
|
164
170
|
// Seed the live DNS zone files from their templates BEFORE the compose
|
|
165
171
|
// mounts them. config/dns/{iamtheinternet.org,example.net}.zone are
|
|
166
172
|
// gitignored runtime state (scrubDnsZones rewrites them per-test); on a
|