@serfab/cadre-host 1.13.0 → 1.15.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.
- package/README.md +347 -423
- package/dist/bin/host.d.ts +6 -3
- package/dist/bin/host.js +332 -524
- package/dist/bin/host.js.map +1 -1
- package/dist/bin/join-output.d.ts +56 -0
- package/dist/bin/join-output.js +92 -0
- package/dist/bin/join-output.js.map +1 -0
- package/dist/bin/nat-output.d.ts +54 -0
- package/dist/bin/nat-output.js +138 -0
- package/dist/bin/nat-output.js.map +1 -0
- package/dist/bin/ui-access.d.ts +22 -0
- package/dist/bin/ui-access.js +44 -0
- package/dist/bin/ui-access.js.map +1 -0
- package/dist/hosted/claim-details.d.ts +66 -0
- package/dist/hosted/claim-details.js +60 -0
- package/dist/hosted/claim-details.js.map +1 -0
- package/dist/hosted/hosted-node-service.d.ts +325 -0
- package/dist/hosted/hosted-node-service.js +678 -0
- package/dist/hosted/hosted-node-service.js.map +1 -0
- package/dist/hosted/hosted-node-store.d.ts +40 -0
- package/dist/hosted/hosted-node-store.js +126 -0
- package/dist/hosted/hosted-node-store.js.map +1 -0
- package/dist/{donation/donation-supervisor.d.ts → hosted/hosted-node-supervisor.d.ts} +70 -53
- package/dist/hosted/hosted-node-supervisor.js +396 -0
- package/dist/hosted/hosted-node-supervisor.js.map +1 -0
- package/dist/hosted/hosted-node-watcher.d.ts +95 -0
- package/dist/hosted/hosted-node-watcher.js +260 -0
- package/dist/hosted/hosted-node-watcher.js.map +1 -0
- package/dist/hosted/index.d.ts +16 -0
- package/dist/hosted/index.js +13 -0
- package/dist/hosted/index.js.map +1 -0
- package/dist/hosted/node-status.d.ts +51 -0
- package/dist/hosted/node-status.js +77 -0
- package/dist/hosted/node-status.js.map +1 -0
- package/dist/hosted/types.d.ts +141 -0
- package/dist/hosted/types.js +22 -0
- package/dist/hosted/types.js.map +1 -0
- package/dist/index.d.ts +12 -24
- package/dist/index.js +9 -19
- package/dist/index.js.map +1 -1
- package/dist/installer/config.d.ts +27 -34
- package/dist/installer/config.js +46 -15
- package/dist/installer/config.js.map +1 -1
- package/dist/installer/index.d.ts +9 -19
- package/dist/installer/index.js +21 -85
- package/dist/installer/index.js.map +1 -1
- package/dist/installer/paths.d.ts +0 -2
- package/dist/installer/paths.js +0 -4
- package/dist/installer/paths.js.map +1 -1
- package/dist/installer/wizard.d.ts +12 -9
- package/dist/installer/wizard.js +15 -14
- package/dist/installer/wizard.js.map +1 -1
- package/dist/nat/address-resolver.d.ts +18 -17
- package/dist/nat/address-resolver.js +79 -25
- package/dist/nat/address-resolver.js.map +1 -1
- package/dist/nat/address-watch.d.ts +37 -0
- package/dist/nat/address-watch.js +84 -0
- package/dist/nat/address-watch.js.map +1 -0
- package/dist/nat/ddns/duckdns.d.ts +2 -1
- package/dist/nat/ddns/duckdns.js +5 -1
- package/dist/nat/ddns/duckdns.js.map +1 -1
- package/dist/nat/ddns/index.d.ts +6 -0
- package/dist/nat/ddns/index.js.map +1 -1
- package/dist/nat/index.d.ts +13 -9
- package/dist/nat/index.js +7 -5
- package/dist/nat/index.js.map +1 -1
- package/dist/nat/nat-service.d.ts +220 -77
- package/dist/nat/nat-service.js +653 -239
- package/dist/nat/nat-service.js.map +1 -1
- package/dist/nat/nat-store.d.ts +12 -5
- package/dist/nat/nat-store.js +124 -29
- package/dist/nat/nat-store.js.map +1 -1
- package/dist/nat/network-mode.d.ts +19 -0
- package/dist/nat/network-mode.js +32 -0
- package/dist/nat/network-mode.js.map +1 -0
- package/dist/nat/port-mapper.d.ts +83 -79
- package/dist/nat/port-mapper.js +148 -189
- package/dist/nat/port-mapper.js.map +1 -1
- package/dist/nat/reachability.d.ts +32 -15
- package/dist/nat/reachability.js +110 -22
- package/dist/nat/reachability.js.map +1 -1
- package/dist/nat/types.d.ts +98 -27
- package/dist/nat/types.js +8 -5
- package/dist/nat/types.js.map +1 -1
- package/dist/orchestrator/host-process-orchestrator.d.ts +32 -74
- package/dist/orchestrator/host-process-orchestrator.js +84 -233
- package/dist/orchestrator/host-process-orchestrator.js.map +1 -1
- package/dist/{installer/identity.d.ts → orchestrator/identity-file.d.ts} +5 -5
- package/dist/{installer/identity.js → orchestrator/identity-file.js} +6 -6
- package/dist/orchestrator/identity-file.js.map +1 -0
- package/dist/orchestrator/index.d.ts +2 -3
- package/dist/orchestrator/index.js +1 -1
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/node-identity.d.ts +6 -6
- package/dist/orchestrator/node-identity.js +7 -7
- package/dist/orchestrator/node-identity.js.map +1 -1
- package/dist/orchestrator/port-allocator.d.ts +5 -6
- package/dist/orchestrator/port-allocator.js +9 -11
- package/dist/orchestrator/port-allocator.js.map +1 -1
- package/dist/orchestrator/state-store.d.ts +3 -5
- package/dist/orchestrator/state-store.js +2 -10
- package/dist/orchestrator/state-store.js.map +1 -1
- package/dist/orchestrator/types.d.ts +33 -22
- package/dist/orchestrator/types.js.map +1 -1
- package/dist/package-version.d.ts +1 -0
- package/dist/package-version.js +4 -0
- package/dist/package-version.js.map +1 -0
- package/dist/server/error-handler.d.ts +5 -25
- package/dist/server/error-handler.js +11 -74
- package/dist/server/error-handler.js.map +1 -1
- package/dist/server/events/types.d.ts +7 -11
- package/dist/server/events/types.js +2 -1
- package/dist/server/events/types.js.map +1 -1
- package/dist/server/index.d.ts +11 -37
- package/dist/server/index.js +30 -48
- package/dist/server/index.js.map +1 -1
- package/dist/server/routes/hosted-nodes.d.ts +28 -0
- package/dist/server/routes/hosted-nodes.js +68 -0
- package/dist/server/routes/hosted-nodes.js.map +1 -0
- package/dist/server/routes/nat.d.ts +4 -1
- package/dist/server/routes/nat.js +13 -2
- package/dist/server/routes/nat.js.map +1 -1
- package/dist/server/routes/nodes.d.ts +6 -20
- package/dist/server/routes/nodes.js +7 -86
- package/dist/server/routes/nodes.js.map +1 -1
- package/dist/server/routes/settings.d.ts +3 -7
- package/dist/server/routes/settings.js +18 -8
- package/dist/server/routes/settings.js.map +1 -1
- package/dist/server/routes/status.d.ts +2 -25
- package/dist/server/routes/status.js +3 -43
- package/dist/server/routes/status.js.map +1 -1
- package/dist/server/server.js +8 -8
- package/dist/server/settings-store.d.ts +2 -2
- package/dist/server/settings-store.js +2 -2
- package/dist/server/static.js +1 -2
- package/dist/server/static.js.map +1 -1
- package/dist/ui/assets/index-BHd8CWZA.css +1 -0
- package/dist/ui/assets/index-Ccr2GjPT.js +23 -0
- package/dist/ui/assets/index-Ccr2GjPT.js.map +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/update/index.d.ts +2 -2
- package/dist/update/index.js +2 -2
- package/dist/update/store.d.ts +1 -1
- package/dist/update/store.js +1 -1
- package/dist/update/types.d.ts +1 -1
- package/dist/update/types.js +1 -1
- package/package.json +5 -7
- package/src/__tests__/cli-nat.smoke.test.ts +219 -212
- package/src/__tests__/cli.smoke.test.ts +2 -2
- package/src/__tests__/global-setup.ts +2 -3
- package/src/__tests__/orchestrator-claim-secret.test.ts +111 -0
- package/src/__tests__/orchestrator-env-scrub.test.ts +160 -163
- package/src/__tests__/orchestrator-handle-contract.ts +134 -134
- package/src/__tests__/orchestrator-node-identity.test.ts +177 -203
- package/src/__tests__/orchestrator-ports.test.ts +124 -143
- package/src/__tests__/orchestrator-push.test.ts +138 -146
- package/src/__tests__/orchestrator.test.ts +1017 -1063
- package/src/__tests__/ui-access.test.ts +30 -0
- package/src/bin/host.ts +397 -572
- package/src/bin/join-output.ts +120 -0
- package/src/bin/nat-output.ts +188 -0
- package/src/bin/ui-access.ts +50 -0
- package/src/{donation → hosted}/__tests__/fake-orchestrator.test.ts +172 -173
- package/src/{donation → hosted}/__tests__/fake-orchestrator.ts +43 -23
- package/src/hosted/__tests__/hosted-node-service.test.ts +708 -0
- package/src/hosted/__tests__/hosted-node-supervisor.test.ts +475 -0
- package/src/hosted/__tests__/test-invitation.ts +33 -0
- package/src/hosted/claim-details.ts +103 -0
- package/src/hosted/hosted-node-service.ts +814 -0
- package/src/hosted/hosted-node-store.ts +144 -0
- package/src/hosted/hosted-node-supervisor.ts +439 -0
- package/src/hosted/hosted-node-watcher.ts +288 -0
- package/src/hosted/index.ts +53 -0
- package/src/hosted/node-status.ts +97 -0
- package/src/hosted/types.ts +160 -0
- package/src/index.ts +72 -91
- package/src/installer/__tests__/config.test.ts +15 -40
- package/src/installer/__tests__/installer.smoke.test.ts +184 -263
- package/src/installer/__tests__/wizard.test.ts +25 -13
- package/src/installer/config.ts +201 -175
- package/src/installer/index.ts +252 -327
- package/src/installer/paths.ts +104 -109
- package/src/installer/wizard.ts +24 -26
- package/src/nat/__tests__/address-resolver.test.ts +107 -82
- package/src/nat/__tests__/nat-service.test.ts +608 -515
- package/src/nat/__tests__/nat-store.test.ts +49 -12
- package/src/nat/__tests__/network-mode.test.ts +38 -0
- package/src/nat/__tests__/port-mapper.test.ts +30 -191
- package/src/nat/__tests__/reachability.test.ts +96 -13
- package/src/nat/address-resolver.ts +94 -52
- package/src/nat/address-watch.ts +92 -0
- package/src/nat/ddns/duckdns.ts +5 -1
- package/src/nat/ddns/index.ts +6 -0
- package/src/nat/index.ts +33 -13
- package/src/nat/nat-service.ts +942 -501
- package/src/nat/nat-store.ts +126 -31
- package/src/nat/network-mode.ts +36 -0
- package/src/nat/port-mapper.ts +241 -329
- package/src/nat/reachability.ts +155 -34
- package/src/nat/types.ts +110 -37
- package/src/{installer/__tests__/identity.test.ts → orchestrator/__tests__/identity-file.test.ts} +34 -34
- package/src/orchestrator/__tests__/node-identity.test.ts +62 -62
- package/src/orchestrator/host-process-orchestrator.ts +1089 -1260
- package/src/{installer/identity.ts → orchestrator/identity-file.ts} +56 -56
- package/src/orchestrator/index.ts +9 -10
- package/src/orchestrator/node-identity.ts +41 -41
- package/src/orchestrator/port-allocator.ts +71 -73
- package/src/orchestrator/state-store.ts +76 -90
- package/src/orchestrator/types.ts +34 -23
- package/src/package-version.ts +4 -0
- package/src/server/__tests__/bus.test.ts +38 -38
- package/src/server/__tests__/error-handler.test.ts +81 -118
- package/src/server/__tests__/fakes.ts +15 -18
- package/src/server/__tests__/hosted-nodes-route.test.ts +161 -0
- package/src/server/__tests__/nodes-route.test.ts +114 -283
- package/src/server/__tests__/publishers.test.ts +193 -180
- package/src/server/__tests__/server.smoke.test.ts +134 -223
- package/src/server/__tests__/settings-route.test.ts +210 -195
- package/src/server/__tests__/sse-route.test.ts +119 -119
- package/src/server/__tests__/status-route.test.ts +82 -121
- package/src/server/error-handler.ts +112 -178
- package/src/server/events/types.ts +20 -24
- package/src/server/index.ts +223 -267
- package/src/server/routes/hosted-nodes.ts +83 -0
- package/src/server/routes/nat.ts +75 -63
- package/src/server/routes/nodes.ts +96 -188
- package/src/server/routes/settings.ts +20 -15
- package/src/server/routes/status.ts +5 -67
- package/src/server/server.ts +8 -8
- package/src/server/settings-store.ts +2 -2
- package/src/server/static.ts +1 -2
- package/src/update/index.ts +2 -2
- package/src/update/store.ts +1 -1
- package/src/update/types.ts +1 -1
- package/dist/auth/duration.d.ts +0 -1
- package/dist/auth/duration.js +0 -32
- package/dist/auth/duration.js.map +0 -1
- package/dist/auth/index.d.ts +0 -11
- package/dist/auth/index.js +0 -9
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/self-label.d.ts +0 -36
- package/dist/auth/self-label.js +0 -57
- package/dist/auth/self-label.js.map +0 -1
- package/dist/auth/trust-circle-store.d.ts +0 -26
- package/dist/auth/trust-circle-store.js +0 -129
- package/dist/auth/trust-circle-store.js.map +0 -1
- package/dist/auth/trust-circle.d.ts +0 -156
- package/dist/auth/trust-circle.js +0 -307
- package/dist/auth/trust-circle.js.map +0 -1
- package/dist/auth/types.d.ts +0 -84
- package/dist/auth/types.js +0 -24
- package/dist/auth/types.js.map +0 -1
- package/dist/donation/donation-service.d.ts +0 -389
- package/dist/donation/donation-service.js +0 -828
- package/dist/donation/donation-service.js.map +0 -1
- package/dist/donation/donation-store.d.ts +0 -51
- package/dist/donation/donation-store.js +0 -152
- package/dist/donation/donation-store.js.map +0 -1
- package/dist/donation/donation-supervisor.js +0 -364
- package/dist/donation/donation-supervisor.js.map +0 -1
- package/dist/donation/grant-service.d.ts +0 -80
- package/dist/donation/grant-service.js +0 -210
- package/dist/donation/grant-service.js.map +0 -1
- package/dist/donation/grant-store.d.ts +0 -38
- package/dist/donation/grant-store.js +0 -135
- package/dist/donation/grant-store.js.map +0 -1
- package/dist/donation/index.d.ts +0 -20
- package/dist/donation/index.js +0 -17
- package/dist/donation/index.js.map +0 -1
- package/dist/donation/types.d.ts +0 -224
- package/dist/donation/types.js +0 -40
- package/dist/donation/types.js.map +0 -1
- package/dist/installer/identity.js.map +0 -1
- package/dist/owner/index.d.ts +0 -11
- package/dist/owner/index.js +0 -11
- package/dist/owner/index.js.map +0 -1
- package/dist/owner/owner-node-client.d.ts +0 -110
- package/dist/owner/owner-node-client.js +0 -184
- package/dist/owner/owner-node-client.js.map +0 -1
- package/dist/server/routes/bootstrap-node-validation.d.ts +0 -115
- package/dist/server/routes/bootstrap-node-validation.js +0 -197
- package/dist/server/routes/bootstrap-node-validation.js.map +0 -1
- package/dist/server/routes/grants-admin.d.ts +0 -29
- package/dist/server/routes/grants-admin.js +0 -58
- package/dist/server/routes/grants-admin.js.map +0 -1
- package/dist/server/routes/grants.d.ts +0 -28
- package/dist/server/routes/grants.js +0 -165
- package/dist/server/routes/grants.js.map +0 -1
- package/dist/server/routes/provision-request-validation.d.ts +0 -45
- package/dist/server/routes/provision-request-validation.js +0 -97
- package/dist/server/routes/provision-request-validation.js.map +0 -1
- package/dist/server/routes/strands.d.ts +0 -20
- package/dist/server/routes/strands.js +0 -38
- package/dist/server/routes/strands.js.map +0 -1
- package/dist/server/routes/trust-circle.d.ts +0 -17
- package/dist/server/routes/trust-circle.js +0 -35
- package/dist/server/routes/trust-circle.js.map +0 -1
- package/dist/strands/index.d.ts +0 -7
- package/dist/strands/index.js +0 -6
- package/dist/strands/index.js.map +0 -1
- package/dist/strands/strand-service.d.ts +0 -50
- package/dist/strands/strand-service.js +0 -103
- package/dist/strands/strand-service.js.map +0 -1
- package/dist/strands/types.d.ts +0 -93
- package/dist/strands/types.js +0 -23
- package/dist/strands/types.js.map +0 -1
- package/dist/ui/assets/index-C6nELNfC.js +0 -23
- package/dist/ui/assets/index-C6nELNfC.js.map +0 -1
- package/dist/ui/assets/index-TtIYSeZO.css +0 -1
- package/src/__tests__/cli-invite.smoke.test.ts +0 -192
- package/src/__tests__/host-owner.smoke.test.ts +0 -123
- package/src/__tests__/orchestrator-owner.test.ts +0 -234
- package/src/__tests__/orchestrator-pin-keys.test.ts +0 -152
- package/src/auth/__tests__/duration.test.ts +0 -28
- package/src/auth/__tests__/self-label.test.ts +0 -85
- package/src/auth/__tests__/trust-circle-integration.test.ts +0 -152
- package/src/auth/__tests__/trust-circle-store.test.ts +0 -115
- package/src/auth/__tests__/trust-circle.test.ts +0 -445
- package/src/auth/duration.ts +0 -33
- package/src/auth/index.ts +0 -21
- package/src/auth/self-label.ts +0 -74
- package/src/auth/trust-circle-store.ts +0 -155
- package/src/auth/trust-circle.ts +0 -395
- package/src/auth/types.ts +0 -99
- package/src/donation/__tests__/donation-service.test.ts +0 -1193
- package/src/donation/__tests__/donation-supervisor.test.ts +0 -548
- package/src/donation/__tests__/grant-service.test.ts +0 -213
- package/src/donation/__tests__/grant-store.test.ts +0 -113
- package/src/donation/donation-service.ts +0 -1033
- package/src/donation/donation-store.ts +0 -176
- package/src/donation/donation-supervisor.ts +0 -411
- package/src/donation/grant-service.ts +0 -252
- package/src/donation/grant-store.ts +0 -152
- package/src/donation/index.ts +0 -65
- package/src/donation/types.ts +0 -260
- package/src/owner/__tests__/owner-node-client.test.ts +0 -194
- package/src/owner/index.ts +0 -12
- package/src/owner/owner-node-client.ts +0 -259
- package/src/server/__tests__/bootstrap-node-validation.test.ts +0 -138
- package/src/server/__tests__/grants-admin-route.test.ts +0 -171
- package/src/server/__tests__/grants-route.test.ts +0 -394
- package/src/server/__tests__/strands-route.test.ts +0 -234
- package/src/server/routes/bootstrap-node-validation.ts +0 -206
- package/src/server/routes/grants-admin.ts +0 -74
- package/src/server/routes/grants.ts +0 -193
- package/src/server/routes/provision-request-validation.ts +0 -105
- package/src/server/routes/strands.ts +0 -54
- package/src/server/routes/trust-circle.ts +0 -49
- package/src/strands/__tests__/strand-service.test.ts +0 -165
- package/src/strands/index.ts +0 -14
- package/src/strands/strand-service.ts +0 -135
- package/src/strands/types.ts +0 -102
|
@@ -1,143 +1,124 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The pure port-set logic behind `HostProcessOrchestrator`: the helpers that
|
|
3
|
-
* allocate, hold, release and reuse one node's set of ports, and the listen
|
|
4
|
-
* addresses a child is started with. No child processes — the behaviour these
|
|
5
|
-
* feed is exercised against a stub child in `orchestrator.test.ts`.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, expect, it } from 'vitest';
|
|
9
|
-
|
|
10
|
-
import { PortAllocator } from '@serfab/cadre-provider';
|
|
11
|
-
|
|
12
|
-
import { childListenAddrs } from '../orchestrator/host-process-orchestrator.js';
|
|
13
|
-
import {
|
|
14
|
-
allocateNodePorts,
|
|
15
|
-
releaseNodePorts,
|
|
16
|
-
reserveNodePorts,
|
|
17
|
-
reusedNodePorts,
|
|
18
|
-
} from '../orchestrator/port-allocator.js';
|
|
19
|
-
import type { NodePorts } from '../orchestrator/types.js';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* A port set as `state.json` hands it back from a build that predates the `ws` key:
|
|
23
|
-
* the type says every key is present, the data does not.
|
|
24
|
-
*/
|
|
25
|
-
function portsWithoutWs(ports: Omit<NodePorts, 'ws'>): NodePorts {
|
|
26
|
-
return ports as NodePorts;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
describe('allocateNodePorts', () => {
|
|
30
|
-
it('allocates the
|
|
31
|
-
const a = new PortAllocator(10000, 10010);
|
|
32
|
-
expect(allocateNodePorts(a)).toEqual({ health: 10000, metrics: 10001, p2p: 10002,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// A partial set left reserved would leak from a bounded range on every failed
|
|
36
|
-
// provision — the whole reason this helper exists rather than
|
|
37
|
-
it('is all-or-nothing: an exhausted range releases everything it took', () => {
|
|
38
|
-
const a = new PortAllocator(13000, 13002);
|
|
39
|
-
expect(() => allocateNodePorts(a)).toThrow(/No available ports/);
|
|
40
|
-
expect(a.has(13000)).toBe(false);
|
|
41
|
-
expect(a.has(13001)).toBe(false);
|
|
42
|
-
expect(a.has(13002)).toBe(false);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
expect(a.has(
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
//
|
|
77
|
-
it('
|
|
78
|
-
const a = new PortAllocator(10000, 10010);
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
expect(a.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
it('
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
expect(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
it(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const a = new PortAllocator(10000, 10010);
|
|
127
|
-
expect(allocateNodePorts(a, reused)).toEqual({ health: 10005, metrics: 10006, p2p: 10007, admin: 10008, ws: 10000 });
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('takes the last handle when a state holding duplicates dropped several', () => {
|
|
131
|
-
const older = { health: 10000, metrics: 10001, p2p: 10002, admin: 10003, ws: 10004 };
|
|
132
|
-
expect(reusedNodePorts([{ ports: older }, { ports: previous }])).toEqual(previous);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
describe('childListenAddrs', () => {
|
|
137
|
-
it('names a TCP listener on the p2p port and a WebSocket listener on the ws port, on every interface', () => {
|
|
138
|
-
expect(childListenAddrs({ p2p: 10002, ws: 10004 })).toEqual([
|
|
139
|
-
'/ip4/0.0.0.0/tcp/10002',
|
|
140
|
-
'/ip4/0.0.0.0/tcp/10004/ws',
|
|
141
|
-
]);
|
|
142
|
-
});
|
|
143
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* The pure port-set logic behind `HostProcessOrchestrator`: the helpers that
|
|
3
|
+
* allocate, hold, release and reuse one node's set of ports, and the listen
|
|
4
|
+
* addresses a child is started with. No child processes — the behaviour these
|
|
5
|
+
* feed is exercised against a stub child in `orchestrator.test.ts`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, expect, it } from 'vitest';
|
|
9
|
+
|
|
10
|
+
import { PortAllocator } from '@serfab/cadre-provider';
|
|
11
|
+
|
|
12
|
+
import { childListenAddrs } from '../orchestrator/host-process-orchestrator.js';
|
|
13
|
+
import {
|
|
14
|
+
allocateNodePorts,
|
|
15
|
+
releaseNodePorts,
|
|
16
|
+
reserveNodePorts,
|
|
17
|
+
reusedNodePorts,
|
|
18
|
+
} from '../orchestrator/port-allocator.js';
|
|
19
|
+
import type { NodePorts } from '../orchestrator/types.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A port set as `state.json` hands it back from a build that predates the `ws` key:
|
|
23
|
+
* the type says every key is present, the data does not.
|
|
24
|
+
*/
|
|
25
|
+
function portsWithoutWs(ports: Omit<NodePorts, 'ws'>): NodePorts {
|
|
26
|
+
return ports as NodePorts;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('allocateNodePorts', () => {
|
|
30
|
+
it('allocates the four ports in a fixed order', () => {
|
|
31
|
+
const a = new PortAllocator(10000, 10010);
|
|
32
|
+
expect(allocateNodePorts(a)).toEqual({ health: 10000, metrics: 10001, p2p: 10002, ws: 10003 });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// A partial set left reserved would leak from a bounded range on every failed
|
|
36
|
+
// provision — the whole reason this helper exists rather than four allocates.
|
|
37
|
+
it('is all-or-nothing: an exhausted range releases everything it took', () => {
|
|
38
|
+
const a = new PortAllocator(13000, 13002);
|
|
39
|
+
expect(() => allocateNodePorts(a)).toThrow(/No available ports/);
|
|
40
|
+
expect(a.has(13000)).toBe(false);
|
|
41
|
+
expect(a.has(13001)).toBe(false);
|
|
42
|
+
expect(a.has(13002)).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('honours an override and reserves it', () => {
|
|
46
|
+
const a = new PortAllocator(10000, 10010);
|
|
47
|
+
expect(allocateNodePorts(a, { p2p: 10005 })).toEqual({
|
|
48
|
+
health: 10000,
|
|
49
|
+
metrics: 10001,
|
|
50
|
+
p2p: 10005,
|
|
51
|
+
ws: 10002,
|
|
52
|
+
});
|
|
53
|
+
// Reserved, so no later allocation can hand it out again.
|
|
54
|
+
expect(a.has(10005)).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// A re-spawn passes its previous set as overrides; every one must come back exactly.
|
|
58
|
+
it('returns a full set of overrides exactly, allocating nothing', () => {
|
|
59
|
+
const a = new PortAllocator(10000, 10010);
|
|
60
|
+
const previous = { health: 10006, metrics: 10007, p2p: 10008, ws: 10009 };
|
|
61
|
+
expect(allocateNodePorts(a, previous)).toEqual(previous);
|
|
62
|
+
expect(a.allocate()).toBe(10000);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('reserveNodePorts / releaseNodePorts', () => {
|
|
67
|
+
it('hold and then release every key of the set', () => {
|
|
68
|
+
const a = new PortAllocator(10000, 10010);
|
|
69
|
+
const ports = { health: 10000, metrics: 10001, p2p: 10002, ws: 10003 };
|
|
70
|
+
reserveNodePorts(a, ports);
|
|
71
|
+
for (const port of Object.values(ports)) expect(a.has(port)).toBe(true);
|
|
72
|
+
releaseNodePorts(a, ports);
|
|
73
|
+
for (const port of Object.values(ports)) expect(a.has(port)).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Rehydrating, or dropping, a `state.json` handle written before `ws` existed.
|
|
77
|
+
it('skip a key the set lacks', () => {
|
|
78
|
+
const a = new PortAllocator(10000, 10010);
|
|
79
|
+
const ports = portsWithoutWs({ health: 10000, metrics: 10001, p2p: 10002 });
|
|
80
|
+
reserveNodePorts(a, ports);
|
|
81
|
+
expect(a.has(undefined as unknown as number)).toBe(false);
|
|
82
|
+
expect(a.allocate()).toBe(10003);
|
|
83
|
+
releaseNodePorts(a, ports);
|
|
84
|
+
expect(a.has(10000)).toBe(false);
|
|
85
|
+
expect(a.has(10003)).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('reusedNodePorts', () => {
|
|
90
|
+
const previous = { health: 10005, metrics: 10006, p2p: 10007, ws: 10008 };
|
|
91
|
+
|
|
92
|
+
it("returns the dropped handle's whole port set", () => {
|
|
93
|
+
expect(reusedNodePorts([{ ports: previous }])).toEqual(previous);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('returns nothing when nothing was dropped, so every port is allocated fresh', () => {
|
|
97
|
+
expect(reusedNodePorts([])).toEqual({});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// A handle persisted before `ws` existed: its other ports come back, `ws` is new.
|
|
101
|
+
it('omits a key the dropped handle lacks', () => {
|
|
102
|
+
const legacy = portsWithoutWs({ health: 10005, metrics: 10006, p2p: 10007 });
|
|
103
|
+
const reused = reusedNodePorts([{ ports: legacy }]);
|
|
104
|
+
expect(reused).toEqual({ health: 10005, metrics: 10006, p2p: 10007 });
|
|
105
|
+
expect('ws' in reused).toBe(false);
|
|
106
|
+
|
|
107
|
+
const a = new PortAllocator(10000, 10010);
|
|
108
|
+
expect(allocateNodePorts(a, reused)).toEqual({ health: 10005, metrics: 10006, p2p: 10007, ws: 10000 });
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('takes the last handle when a state holding duplicates dropped several', () => {
|
|
112
|
+
const older = { health: 10000, metrics: 10001, p2p: 10002, ws: 10003 };
|
|
113
|
+
expect(reusedNodePorts([{ ports: older }, { ports: previous }])).toEqual(previous);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('childListenAddrs', () => {
|
|
118
|
+
it('names a TCP listener on the p2p port and a WebSocket listener on the ws port, on every interface', () => {
|
|
119
|
+
expect(childListenAddrs({ p2p: 10002, ws: 10004 })).toEqual([
|
|
120
|
+
'/ip4/0.0.0.0/tcp/10002',
|
|
121
|
+
'/ip4/0.0.0.0/tcp/10004/ws',
|
|
122
|
+
]);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -1,146 +1,138 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HostProcessOrchestrator push-credential injection tests.
|
|
3
|
-
*
|
|
4
|
-
* Verifies the orchestrator writes `config.push` into the spawned node's
|
|
5
|
-
* cadre.json when the pushResolver yields credentials, omits it otherwise,
|
|
6
|
-
* re-resolves on every (re-)spawn, and never persists a raw private key in
|
|
7
|
-
* state.json. Uses a fake CLI entrypoint that just writes its startup token.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
11
|
-
import { mkdtempSync, mkdirSync, rmSync, readFileSync, writeFileSync } from 'node:fs';
|
|
12
|
-
import { tmpdir } from 'node:os';
|
|
13
|
-
import { join } from 'node:path';
|
|
14
|
-
|
|
15
|
-
import type { PushCredentials } from '@serfab/cadre-core';
|
|
16
|
-
import { HostProcessOrchestrator
|
|
17
|
-
import { removeAllNodes } from './orchestrator-teardown.js';
|
|
18
|
-
|
|
19
|
-
const FAKE_CLI = `
|
|
20
|
-
import fs from 'node:fs';
|
|
21
|
-
const args = process.argv.slice(2);
|
|
22
|
-
const get = (flag) => { const i = args.indexOf(flag); return i >= 0 ? args[i + 1] : undefined; };
|
|
23
|
-
const tokenPath = get('--startup-token-file');
|
|
24
|
-
const token = process.env.CADRE_STARTUP_TOKEN ?? '';
|
|
25
|
-
if (tokenPath && token) { try { fs.writeFileSync(tokenPath, token, 'utf8'); } catch (e) { console.error(e); } }
|
|
26
|
-
process.on('SIGTERM', () => process.exit(0));
|
|
27
|
-
setInterval(() => {}, 1 << 30);
|
|
28
|
-
`;
|
|
29
|
-
|
|
30
|
-
const FCM = { projectId: 'proj', clientEmail: 'svc@proj.iam', privateKey: 'FCM-SECRET-PEM' };
|
|
31
|
-
const APNS = { keyId: 'KID', teamId: 'TEAM', bundleId: 'com.example.app', privateKey: 'APNS-SECRET-P8', production: false };
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
let
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
await orch.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
await orch.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
expect(
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('
|
|
97
|
-
const rootDir = join(tmpRoot, '
|
|
98
|
-
const orch = makeOrchestrator(rootDir, async () =>
|
|
99
|
-
await orch.init();
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
expect(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
expect(readChildConfig(rootDir).push).
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
await orch.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
await orch.createContainer({ containerId: 'storage-1', partyId: 'p', bootstrapNodes: [], profile: 'storage' });
|
|
141
|
-
expect(readChildConfig(rootDir, 'storage-1').push).toEqual({ fcm: FCM });
|
|
142
|
-
|
|
143
|
-
await orch.createContainer({ containerId: 'txn-1', partyId: 'p', bootstrapNodes: [], profile: 'transaction' });
|
|
144
|
-
expect(readChildConfig(rootDir, 'txn-1').push).toBeUndefined();
|
|
145
|
-
});
|
|
146
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* HostProcessOrchestrator push-credential injection tests.
|
|
3
|
+
*
|
|
4
|
+
* Verifies the orchestrator writes `config.push` into the spawned node's
|
|
5
|
+
* cadre.json when the pushResolver yields credentials, omits it otherwise,
|
|
6
|
+
* re-resolves on every (re-)spawn, and never persists a raw private key in
|
|
7
|
+
* state.json. Uses a fake CLI entrypoint that just writes its startup token.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
11
|
+
import { mkdtempSync, mkdirSync, rmSync, readFileSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { tmpdir } from 'node:os';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
|
|
15
|
+
import type { PushCredentials } from '@serfab/cadre-core';
|
|
16
|
+
import { HostProcessOrchestrator } from '../orchestrator/host-process-orchestrator.js';
|
|
17
|
+
import { removeAllNodes } from './orchestrator-teardown.js';
|
|
18
|
+
|
|
19
|
+
const FAKE_CLI = `
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
const args = process.argv.slice(2);
|
|
22
|
+
const get = (flag) => { const i = args.indexOf(flag); return i >= 0 ? args[i + 1] : undefined; };
|
|
23
|
+
const tokenPath = get('--startup-token-file');
|
|
24
|
+
const token = process.env.CADRE_STARTUP_TOKEN ?? '';
|
|
25
|
+
if (tokenPath && token) { try { fs.writeFileSync(tokenPath, token, 'utf8'); } catch (e) { console.error(e); } }
|
|
26
|
+
process.on('SIGTERM', () => process.exit(0));
|
|
27
|
+
setInterval(() => {}, 1 << 30);
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
const FCM = { projectId: 'proj', clientEmail: 'svc@proj.iam', privateKey: 'FCM-SECRET-PEM' };
|
|
31
|
+
const APNS = { keyId: 'KID', teamId: 'TEAM', bundleId: 'com.example.app', privateKey: 'APNS-SECRET-P8', production: false };
|
|
32
|
+
/** A storage node — the one profile whose spawn carries push. */
|
|
33
|
+
const NODE = { containerId: 'n1', partyId: 'p', profile: 'storage' as const };
|
|
34
|
+
|
|
35
|
+
let tmpRoot: string;
|
|
36
|
+
let scriptPath: string;
|
|
37
|
+
const orchestrators: HostProcessOrchestrator[] = [];
|
|
38
|
+
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
tmpRoot = mkdtempSync(join(tmpdir(), 'cadre-host-push-'));
|
|
41
|
+
scriptPath = join(tmpRoot, 'fake-cli.mjs');
|
|
42
|
+
writeFileSync(scriptPath, FAKE_CLI, 'utf8');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
afterEach(async () => {
|
|
46
|
+
try {
|
|
47
|
+
await removeAllNodes(orchestrators);
|
|
48
|
+
} finally {
|
|
49
|
+
await sleep(50);
|
|
50
|
+
try { rmSync(tmpRoot, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
function makeOrchestrator(rootDir: string, pushResolver?: () => Promise<PushCredentials | undefined>): HostProcessOrchestrator {
|
|
55
|
+
mkdirSync(rootDir, { recursive: true });
|
|
56
|
+
const orch = new HostProcessOrchestrator({
|
|
57
|
+
rootDir,
|
|
58
|
+
portRange: { start: 18000, end: 18499 },
|
|
59
|
+
stopTimeoutMs: 1500,
|
|
60
|
+
spawn: { entrypoint: scriptPath },
|
|
61
|
+
...(pushResolver ? { pushResolver } : {}),
|
|
62
|
+
});
|
|
63
|
+
orchestrators.push(orch);
|
|
64
|
+
return orch;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function readChildConfig(rootDir: string, containerId = NODE.containerId): Record<string, unknown> {
|
|
68
|
+
const raw = readFileSync(join(rootDir, containerId, 'cadre.json'), 'utf8');
|
|
69
|
+
return JSON.parse(raw) as Record<string, unknown>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function sleep(ms: number): Promise<void> {
|
|
73
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
describe('HostProcessOrchestrator push injection', () => {
|
|
77
|
+
it('omits config.push when no resolver is configured', async () => {
|
|
78
|
+
const rootDir = join(tmpRoot, 'b');
|
|
79
|
+
const orch = makeOrchestrator(rootDir);
|
|
80
|
+
await orch.init();
|
|
81
|
+
await orch.createContainer(NODE);
|
|
82
|
+
|
|
83
|
+
const cfg = readChildConfig(rootDir);
|
|
84
|
+
expect(cfg.push).toBeUndefined();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('omits config.push when the resolver returns undefined (push opt-in)', async () => {
|
|
88
|
+
const rootDir = join(tmpRoot, 'c');
|
|
89
|
+
const orch = makeOrchestrator(rootDir, async () => undefined);
|
|
90
|
+
await orch.init();
|
|
91
|
+
await orch.createContainer(NODE);
|
|
92
|
+
|
|
93
|
+
expect(readChildConfig(rootDir).push).toBeUndefined();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('degrades to no push (does not fail the spawn) when the resolver throws', async () => {
|
|
97
|
+
const rootDir = join(tmpRoot, 'd');
|
|
98
|
+
const orch = makeOrchestrator(rootDir, async () => { throw new Error('partial creds'); });
|
|
99
|
+
await orch.init();
|
|
100
|
+
// Spawn still succeeds.
|
|
101
|
+
const created = await orch.createContainer(NODE);
|
|
102
|
+
expect(orch.getNode(created.dockerId)?.status).toBe('running');
|
|
103
|
+
expect(readChildConfig(rootDir).push).toBeUndefined();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('re-resolves on re-spawn and never persists a raw private key in state.json', async () => {
|
|
107
|
+
const rootDir = join(tmpRoot, 'e');
|
|
108
|
+
let current: PushCredentials | undefined = { fcm: FCM };
|
|
109
|
+
const orch = makeOrchestrator(rootDir, async () => current);
|
|
110
|
+
await orch.init();
|
|
111
|
+
const first = await orch.createContainer(NODE);
|
|
112
|
+
expect(readChildConfig(rootDir).push).toEqual({ fcm: FCM });
|
|
113
|
+
|
|
114
|
+
// state.json must not carry the raw key — only re-resolvable references.
|
|
115
|
+
const stateRaw = readFileSync(join(rootDir, 'state.json'), 'utf8');
|
|
116
|
+
expect(stateRaw).not.toContain('FCM-SECRET-PEM');
|
|
117
|
+
|
|
118
|
+
// Rotate the resolver's answer, then re-spawn — the new spawn must pick it up.
|
|
119
|
+
current = { apns: APNS };
|
|
120
|
+
await orch.stopContainer(first.dockerId);
|
|
121
|
+
await orch.createContainer(NODE);
|
|
122
|
+
const cfg2 = readChildConfig(rootDir);
|
|
123
|
+
expect(cfg2.push).toEqual({ apns: APNS });
|
|
124
|
+
expect((cfg2.push as PushCredentials).fcm).toBeUndefined();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('injects push for a storage-profile managed node but not a transaction node', async () => {
|
|
128
|
+
const rootDir = join(tmpRoot, 'f');
|
|
129
|
+
const orch = makeOrchestrator(rootDir, async () => ({ fcm: FCM, apns: APNS, cooldownMs: 1000 }));
|
|
130
|
+
await orch.init();
|
|
131
|
+
|
|
132
|
+
await orch.createContainer({ containerId: 'storage-1', partyId: 'p', profile: 'storage' });
|
|
133
|
+
expect(readChildConfig(rootDir, 'storage-1').push).toEqual({ fcm: FCM, apns: APNS, cooldownMs: 1000 });
|
|
134
|
+
|
|
135
|
+
await orch.createContainer({ containerId: 'txn-1', partyId: 'p', profile: 'transaction' });
|
|
136
|
+
expect(readChildConfig(rootDir, 'txn-1').push).toBeUndefined();
|
|
137
|
+
});
|
|
138
|
+
});
|