@celilo/cli 0.26.1 → 0.27.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/package.json +3 -3
- package/src/__integration__/container-services-cli.integration.test.ts +0 -4
- package/src/ansible/dependencies.test.ts +233 -289
- package/src/ansible/dependencies.ts +151 -83
- package/src/cli/commands/alerts-sweep.ts +14 -3
- package/src/cli/commands/machine-add.ts +0 -1
- package/src/cli/commands/machine-list.ts +10 -4
- package/src/cli/commands/machine-remove.ts +13 -7
- package/src/cli/commands/machine-status.ts +9 -11
- package/src/db/schema.ts +6 -4
- package/src/hooks/capability-loader.ts +6 -0
- package/src/hooks/define-hook.test.ts +4 -0
- package/src/infrastructure/property-extractor.test.ts +0 -2
- package/src/services/alerting/sweep-runner.test.ts +5 -1
- package/src/services/alerting/sweep-runner.ts +14 -8
- package/src/services/aspect-runner.test.ts +0 -1
- package/src/services/infrastructure-selector.test.ts +0 -7
- package/src/services/infrastructure-selector.ts +24 -25
- package/src/services/infrastructure-variable-resolver.test.ts +0 -6
- package/src/services/infrastructure-variable-resolver.ts +0 -3
- package/src/services/machine-pool.test.ts +53 -85
- package/src/services/machine-pool.ts +68 -84
- package/src/services/module-deploy.ts +17 -39
- package/src/services/ssh-key-manager.test.ts +0 -10
- package/src/types/infrastructure.ts +11 -1
|
@@ -100,7 +100,6 @@ describe('infrastructure-selector', () => {
|
|
|
100
100
|
hardware: { cpu_cores: 4, memory_mb: 4096, disk_gb: 128 },
|
|
101
101
|
role: 'host',
|
|
102
102
|
interfaces: [],
|
|
103
|
-
assignedModuleIds: [],
|
|
104
103
|
});
|
|
105
104
|
|
|
106
105
|
const result = await selectInfrastructure(module);
|
|
@@ -143,7 +142,6 @@ describe('infrastructure-selector', () => {
|
|
|
143
142
|
hardware: { cpu_cores: 4, memory_mb: 4096, disk_gb: 128 },
|
|
144
143
|
role: 'host',
|
|
145
144
|
interfaces: [],
|
|
146
|
-
assignedModuleIds: [],
|
|
147
145
|
});
|
|
148
146
|
|
|
149
147
|
const result = await selectInfrastructure(module);
|
|
@@ -308,7 +306,6 @@ describe('infrastructure-selector', () => {
|
|
|
308
306
|
hardware: { cpu_cores: 1, memory_mb: 4096, disk_gb: 128 },
|
|
309
307
|
role: 'host',
|
|
310
308
|
interfaces: [],
|
|
311
|
-
assignedModuleIds: [],
|
|
312
309
|
});
|
|
313
310
|
|
|
314
311
|
// Should throw InfrastructureError with resource details
|
|
@@ -350,7 +347,6 @@ describe('infrastructure-selector', () => {
|
|
|
350
347
|
hardware: { cpu_cores: 4, memory_mb: 1024, disk_gb: 128 },
|
|
351
348
|
role: 'host',
|
|
352
349
|
interfaces: [],
|
|
353
|
-
assignedModuleIds: [],
|
|
354
350
|
});
|
|
355
351
|
|
|
356
352
|
await expect(selectInfrastructure(module)).rejects.toThrow(InfrastructureError);
|
|
@@ -390,7 +386,6 @@ describe('infrastructure-selector', () => {
|
|
|
390
386
|
hardware: { cpu_cores: 4, memory_mb: 4096, disk_gb: 10 },
|
|
391
387
|
role: 'host',
|
|
392
388
|
interfaces: [],
|
|
393
|
-
assignedModuleIds: [],
|
|
394
389
|
});
|
|
395
390
|
|
|
396
391
|
await expect(selectInfrastructure(module)).rejects.toThrow(InfrastructureError);
|
|
@@ -430,7 +425,6 @@ describe('infrastructure-selector', () => {
|
|
|
430
425
|
hardware: { cpu_cores: 8, memory_mb: 16384, disk_gb: 256 },
|
|
431
426
|
role: 'host',
|
|
432
427
|
interfaces: [],
|
|
433
|
-
assignedModuleIds: [],
|
|
434
428
|
});
|
|
435
429
|
|
|
436
430
|
const result = await selectInfrastructure(module);
|
|
@@ -473,7 +467,6 @@ describe('infrastructure-selector', () => {
|
|
|
473
467
|
hardware: { cpu_cores: 4, memory_mb: 4096, disk_gb: 128 },
|
|
474
468
|
role: 'host',
|
|
475
469
|
interfaces: [],
|
|
476
|
-
assignedModuleIds: [],
|
|
477
470
|
});
|
|
478
471
|
|
|
479
472
|
const result = await selectInfrastructure(module);
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
ResourceRequirements,
|
|
8
8
|
} from '../types/infrastructure';
|
|
9
9
|
import { listContainerServices } from './container-service';
|
|
10
|
-
import {
|
|
10
|
+
import { getModulesOnMachine, listMachines } from './machine-pool';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Infrastructure selection error
|
|
@@ -49,25 +49,24 @@ function getResourceRequirements(module: Module): ResourceRequirements {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
52
|
+
* Is this machine big enough for the module at all?
|
|
53
|
+
*
|
|
54
|
+
* Deliberately NOT multi-tenant capacity accounting (celilo#773). It used to
|
|
55
|
+
* subtract an "already allocated" figure that was hard-coded to zero behind a
|
|
56
|
+
* TODO, so the subtraction never changed an answer and the check has always
|
|
57
|
+
* been exactly this comparison. Saying so is the honest version; a gate that
|
|
58
|
+
* cannot fail reads as protection that is not there (Rule 7.6).
|
|
59
|
+
*
|
|
60
|
+
* Real accounting needs a decision nobody has made — whether a module's draw is
|
|
61
|
+
* its manifest MINIMUM (`requires.system`) or its deployed size, which on a
|
|
62
|
+
* pool machine celilo does not own. Until then the occupancy filter, which IS
|
|
63
|
+
* now correct, is what keeps two modules off one box.
|
|
53
64
|
*/
|
|
54
|
-
|
|
55
|
-
machine: Machine,
|
|
56
|
-
requirements: ResourceRequirements,
|
|
57
|
-
): Promise<boolean> {
|
|
58
|
-
// Get already-allocated resources
|
|
59
|
-
const allocated = await getModuleResourcesOnMachine(machine.id);
|
|
60
|
-
|
|
61
|
-
// Calculate remaining capacity
|
|
62
|
-
const remainingCpu = machine.hardware.cpu_cores - allocated.cpu;
|
|
63
|
-
const remainingMemory = machine.hardware.memory_mb - allocated.memory;
|
|
64
|
-
const remainingDisk = machine.hardware.disk_gb - allocated.disk;
|
|
65
|
-
|
|
66
|
-
// Check if machine has enough remaining resources
|
|
65
|
+
function machineHasCapacity(machine: Machine, requirements: ResourceRequirements): boolean {
|
|
67
66
|
return (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
machine.hardware.cpu_cores >= requirements.cpu &&
|
|
68
|
+
machine.hardware.memory_mb >= requirements.memory &&
|
|
69
|
+
machine.hardware.disk_gb >= requirements.disk
|
|
71
70
|
);
|
|
72
71
|
}
|
|
73
72
|
|
|
@@ -181,11 +180,12 @@ export async function selectInfrastructure(module: Module): Promise<Infrastructu
|
|
|
181
180
|
continue;
|
|
182
181
|
}
|
|
183
182
|
// Skip machines already assigned to other modules
|
|
184
|
-
|
|
183
|
+
const occupants = getModulesOnMachine(machine.id);
|
|
184
|
+
if (occupants.length > 0 && !occupants.includes(moduleId)) {
|
|
185
185
|
rejections.push({
|
|
186
186
|
hostname: machine.hostname,
|
|
187
187
|
ipAddress: machine.ipAddress,
|
|
188
|
-
reason: `already assigned to module(s): ${
|
|
188
|
+
reason: `already assigned to module(s): ${occupants.join(', ')}`,
|
|
189
189
|
});
|
|
190
190
|
continue;
|
|
191
191
|
}
|
|
@@ -208,14 +208,13 @@ export async function selectInfrastructure(module: Module): Promise<Infrastructu
|
|
|
208
208
|
});
|
|
209
209
|
continue;
|
|
210
210
|
}
|
|
211
|
-
if (
|
|
211
|
+
if (machineHasCapacity(machine, requirements)) {
|
|
212
212
|
availableMachines.push(machine);
|
|
213
213
|
} else {
|
|
214
|
-
const allocated = await getModuleResourcesOnMachine(machine.id);
|
|
215
214
|
const shortfalls: string[] = [];
|
|
216
|
-
const remainingCpu = machine.hardware.cpu_cores
|
|
217
|
-
const remainingMemory = machine.hardware.memory_mb
|
|
218
|
-
const remainingDisk = machine.hardware.disk_gb
|
|
215
|
+
const remainingCpu = machine.hardware.cpu_cores;
|
|
216
|
+
const remainingMemory = machine.hardware.memory_mb;
|
|
217
|
+
const remainingDisk = machine.hardware.disk_gb;
|
|
219
218
|
|
|
220
219
|
if (remainingCpu < requirements.cpu) {
|
|
221
220
|
shortfalls.push(`CPU: ${remainingCpu} available, ${requirements.cpu} required`);
|
|
@@ -96,7 +96,6 @@ describe('resolveInfrastructureVariables - Machine Infrastructure', () => {
|
|
|
96
96
|
sshKeyEncrypted: 'encrypted-key',
|
|
97
97
|
hardware: { cpu_cores: 2, memory_mb: 2048, disk_gb: 20 },
|
|
98
98
|
zone: 'external',
|
|
99
|
-
assignedModuleIds: [],
|
|
100
99
|
});
|
|
101
100
|
|
|
102
101
|
// Create module infrastructure selection
|
|
@@ -169,7 +168,6 @@ describe('resolveInfrastructureVariables - Machine Infrastructure', () => {
|
|
|
169
168
|
sshKeyEncrypted: 'encrypted',
|
|
170
169
|
hardware: { cpu_cores: 1, memory_mb: 1024, disk_gb: 10 },
|
|
171
170
|
zone: 'internal',
|
|
172
|
-
assignedModuleIds: [],
|
|
173
171
|
});
|
|
174
172
|
|
|
175
173
|
await db.insert(moduleInfrastructure).values({
|
|
@@ -468,7 +466,6 @@ describe('resolveInfrastructureVariables - User Override', () => {
|
|
|
468
466
|
sshKeyEncrypted: 'encrypted',
|
|
469
467
|
hardware: { cpu_cores: 2, memory_mb: 2048, disk_gb: 20 },
|
|
470
468
|
zone: 'external',
|
|
471
|
-
assignedModuleIds: [],
|
|
472
469
|
});
|
|
473
470
|
|
|
474
471
|
await db.insert(moduleInfrastructure).values({
|
|
@@ -526,7 +523,6 @@ describe('resolveInfrastructureVariables - Required vs Optional', () => {
|
|
|
526
523
|
sshKeyEncrypted: 'encrypted',
|
|
527
524
|
hardware: { cpu_cores: 2, memory_mb: 2048, disk_gb: 20 },
|
|
528
525
|
zone: 'internal',
|
|
529
|
-
assignedModuleIds: [],
|
|
530
526
|
});
|
|
531
527
|
|
|
532
528
|
await db.insert(moduleInfrastructure).values({
|
|
@@ -583,7 +579,6 @@ describe('resolveInfrastructureVariables - Required vs Optional', () => {
|
|
|
583
579
|
sshKeyEncrypted: 'encrypted',
|
|
584
580
|
hardware: { cpu_cores: 2, memory_mb: 2048, disk_gb: 20 },
|
|
585
581
|
zone: 'internal',
|
|
586
|
-
assignedModuleIds: [],
|
|
587
582
|
});
|
|
588
583
|
|
|
589
584
|
await db.insert(moduleInfrastructure).values({
|
|
@@ -694,7 +689,6 @@ describe('resolveInfrastructureVariables - Edge Cases', () => {
|
|
|
694
689
|
sshKeyEncrypted: 'encrypted',
|
|
695
690
|
hardware: { cpu_cores: 2, memory_mb: 2048, disk_gb: 20 },
|
|
696
691
|
zone: 'internal',
|
|
697
|
-
assignedModuleIds: [],
|
|
698
692
|
});
|
|
699
693
|
|
|
700
694
|
await db.insert(moduleInfrastructure).values({
|
|
@@ -90,9 +90,6 @@ export async function resolveInfrastructureVariables(
|
|
|
90
90
|
hardware: machineRow.hardware || { cpu_cores: 0, memory_mb: 0, disk_gb: 0 },
|
|
91
91
|
role: (machineRow.role as Machine['role']) || 'host',
|
|
92
92
|
interfaces: (machineRow.interfaces || []) as Machine['interfaces'],
|
|
93
|
-
assignedModuleIds: Array.isArray(machineRow.assignedModuleIds)
|
|
94
|
-
? machineRow.assignedModuleIds
|
|
95
|
-
: [],
|
|
96
93
|
earmarkedModule: machineRow.earmarkedModule ?? undefined,
|
|
97
94
|
createdAt: new Date(machineRow.createdAt),
|
|
98
95
|
updatedAt: new Date(machineRow.updatedAt),
|
|
@@ -7,18 +7,17 @@ import { mkdtempSync, rmSync } from 'node:fs';
|
|
|
7
7
|
import { tmpdir } from 'node:os';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
import { eq } from 'drizzle-orm';
|
|
10
|
-
import { closeDb, createDbClient } from '../db/client';
|
|
10
|
+
import { closeDb, createDbClient, getDb } from '../db/client';
|
|
11
11
|
import { runMigrations } from '../db/migrate';
|
|
12
|
-
import { machines } from '../db/schema';
|
|
12
|
+
import { machines, moduleInfrastructure, modules } from '../db/schema';
|
|
13
13
|
import {
|
|
14
14
|
addMachine,
|
|
15
|
-
assignModuleToMachine,
|
|
16
15
|
getMachine,
|
|
17
16
|
getMachineByHostname,
|
|
18
17
|
getMachineSshKey,
|
|
18
|
+
getModulesOnMachine,
|
|
19
19
|
listMachines,
|
|
20
20
|
removeMachine,
|
|
21
|
-
unassignModuleFromMachine,
|
|
22
21
|
} from './machine-pool';
|
|
23
22
|
|
|
24
23
|
describe('machine-pool', () => {
|
|
@@ -72,7 +71,6 @@ describe('machine-pool', () => {
|
|
|
72
71
|
},
|
|
73
72
|
role: 'host',
|
|
74
73
|
interfaces: [],
|
|
75
|
-
assignedModuleIds: [],
|
|
76
74
|
});
|
|
77
75
|
|
|
78
76
|
expect(machine.id).toBeDefined();
|
|
@@ -80,7 +78,6 @@ describe('machine-pool', () => {
|
|
|
80
78
|
expect(machine.zone).toBe('internal');
|
|
81
79
|
expect(machine.ipAddress).toBe('192.168.1.100');
|
|
82
80
|
expect(machine.hardware.cpu_cores).toBe(4);
|
|
83
|
-
expect(machine.assignedModuleIds).toEqual([]);
|
|
84
81
|
expect(machine.createdAt).toBeInstanceOf(Date);
|
|
85
82
|
});
|
|
86
83
|
|
|
@@ -94,7 +91,6 @@ describe('machine-pool', () => {
|
|
|
94
91
|
hardware: { cpu_cores: 2, memory_mb: 2048, disk_gb: 64 },
|
|
95
92
|
role: 'host',
|
|
96
93
|
interfaces: [],
|
|
97
|
-
assignedModuleIds: [],
|
|
98
94
|
});
|
|
99
95
|
|
|
100
96
|
// Verify key is encrypted in database
|
|
@@ -115,7 +111,6 @@ describe('machine-pool', () => {
|
|
|
115
111
|
hardware: { cpu_cores: 2, memory_mb: 2048, disk_gb: 64 },
|
|
116
112
|
role: 'host',
|
|
117
113
|
interfaces: [],
|
|
118
|
-
assignedModuleIds: [],
|
|
119
114
|
});
|
|
120
115
|
|
|
121
116
|
const externalMachine = await addMachine({
|
|
@@ -127,7 +122,6 @@ describe('machine-pool', () => {
|
|
|
127
122
|
hardware: { cpu_cores: 1, memory_mb: 1024, disk_gb: 25 },
|
|
128
123
|
role: 'host',
|
|
129
124
|
interfaces: [],
|
|
130
|
-
assignedModuleIds: [],
|
|
131
125
|
});
|
|
132
126
|
|
|
133
127
|
expect(dmzMachine.zone).toBe('dmz');
|
|
@@ -146,7 +140,6 @@ describe('machine-pool', () => {
|
|
|
146
140
|
hardware: { cpu_cores: 2, memory_mb: 2048, disk_gb: 64 },
|
|
147
141
|
role: 'host',
|
|
148
142
|
interfaces: [],
|
|
149
|
-
assignedModuleIds: [],
|
|
150
143
|
});
|
|
151
144
|
|
|
152
145
|
const retrieved = await getMachine(created.id);
|
|
@@ -173,7 +166,6 @@ describe('machine-pool', () => {
|
|
|
173
166
|
hardware: { cpu_cores: 4, memory_mb: 4096, disk_gb: 128 },
|
|
174
167
|
role: 'host',
|
|
175
168
|
interfaces: [],
|
|
176
|
-
assignedModuleIds: [],
|
|
177
169
|
});
|
|
178
170
|
|
|
179
171
|
const retrieved = await getMachineByHostname('unique-hostname');
|
|
@@ -204,7 +196,6 @@ describe('machine-pool', () => {
|
|
|
204
196
|
hardware: { cpu_cores: 2, memory_mb: 2048, disk_gb: 64 },
|
|
205
197
|
role: 'host',
|
|
206
198
|
interfaces: [],
|
|
207
|
-
assignedModuleIds: [],
|
|
208
199
|
});
|
|
209
200
|
|
|
210
201
|
await addMachine({
|
|
@@ -216,7 +207,6 @@ describe('machine-pool', () => {
|
|
|
216
207
|
hardware: { cpu_cores: 4, memory_mb: 4096, disk_gb: 128 },
|
|
217
208
|
role: 'host',
|
|
218
209
|
interfaces: [],
|
|
219
|
-
assignedModuleIds: [],
|
|
220
210
|
});
|
|
221
211
|
|
|
222
212
|
const result = await listMachines();
|
|
@@ -233,7 +223,6 @@ describe('machine-pool', () => {
|
|
|
233
223
|
hardware: { cpu_cores: 2, memory_mb: 2048, disk_gb: 64 },
|
|
234
224
|
role: 'host',
|
|
235
225
|
interfaces: [],
|
|
236
|
-
assignedModuleIds: [],
|
|
237
226
|
});
|
|
238
227
|
|
|
239
228
|
await addMachine({
|
|
@@ -245,7 +234,6 @@ describe('machine-pool', () => {
|
|
|
245
234
|
hardware: { cpu_cores: 1, memory_mb: 1024, disk_gb: 25 },
|
|
246
235
|
role: 'host',
|
|
247
236
|
interfaces: [],
|
|
248
|
-
assignedModuleIds: [],
|
|
249
237
|
});
|
|
250
238
|
|
|
251
239
|
const dmzMachines = await listMachines({ zone: 'dmz' });
|
|
@@ -269,7 +257,6 @@ describe('machine-pool', () => {
|
|
|
269
257
|
hardware: { cpu_cores: 2, memory_mb: 2048, disk_gb: 64 },
|
|
270
258
|
role: 'host',
|
|
271
259
|
interfaces: [],
|
|
272
|
-
assignedModuleIds: [],
|
|
273
260
|
});
|
|
274
261
|
|
|
275
262
|
const decryptedKey = await getMachineSshKey(machine.id);
|
|
@@ -281,10 +268,19 @@ describe('machine-pool', () => {
|
|
|
281
268
|
});
|
|
282
269
|
});
|
|
283
270
|
|
|
284
|
-
|
|
285
|
-
|
|
271
|
+
/**
|
|
272
|
+
* celilo#773. `assignModuleToMachine` / `unassignModuleFromMachine` are gone
|
|
273
|
+
* along with the column they wrote — both were exported, neither had a single
|
|
274
|
+
* caller, and the column they maintained had an append-only writer elsewhere
|
|
275
|
+
* with no removal path at all.
|
|
276
|
+
*
|
|
277
|
+
* Occupancy is derived now, so these assert the two divergences that actually
|
|
278
|
+
* bit the fleet rather than that a setter sets.
|
|
279
|
+
*/
|
|
280
|
+
describe('getModulesOnMachine', () => {
|
|
281
|
+
async function machineWithModuleDeployed(hostname: string, moduleId: string) {
|
|
286
282
|
const machine = await addMachine({
|
|
287
|
-
hostname
|
|
283
|
+
hostname,
|
|
288
284
|
zone: 'internal',
|
|
289
285
|
ipAddress: '192.168.1.100',
|
|
290
286
|
sshUser: 'ubuntu',
|
|
@@ -292,86 +288,59 @@ describe('machine-pool', () => {
|
|
|
292
288
|
hardware: { cpu_cores: 4, memory_mb: 4096, disk_gb: 128 },
|
|
293
289
|
role: 'host',
|
|
294
290
|
interfaces: [],
|
|
295
|
-
assignedModuleIds: [],
|
|
296
291
|
});
|
|
292
|
+
const db = getDb();
|
|
293
|
+
db.insert(modules)
|
|
294
|
+
.values({
|
|
295
|
+
id: moduleId,
|
|
296
|
+
name: moduleId,
|
|
297
|
+
version: '1.0.0',
|
|
298
|
+
manifestData: {},
|
|
299
|
+
sourcePath: `/tmp/${moduleId}`,
|
|
300
|
+
})
|
|
301
|
+
.run();
|
|
302
|
+
db.insert(moduleInfrastructure)
|
|
303
|
+
.values({
|
|
304
|
+
id: `infra-${moduleId}`,
|
|
305
|
+
moduleId,
|
|
306
|
+
infrastructureType: 'machine',
|
|
307
|
+
machineId: machine.id,
|
|
308
|
+
})
|
|
309
|
+
.run();
|
|
310
|
+
return machine;
|
|
311
|
+
}
|
|
297
312
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
313
|
+
it('reports a machine that is hosting a module as occupied', async () => {
|
|
314
|
+
// The live case: briq hosted a VERIFIED iptables and reported
|
|
315
|
+
// "None (available)", so a second module could be placed on top of it.
|
|
316
|
+
const machine = await machineWithModuleDeployed('briq', 'iptables');
|
|
317
|
+
expect(getModulesOnMachine(machine.id)).toEqual(['iptables']);
|
|
302
318
|
});
|
|
303
319
|
|
|
304
|
-
it('
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
sshKey: 'test-key',
|
|
311
|
-
hardware: { cpu_cores: 8, memory_mb: 16384, disk_gb: 256 },
|
|
312
|
-
role: 'host',
|
|
313
|
-
interfaces: [],
|
|
314
|
-
assignedModuleIds: [],
|
|
315
|
-
});
|
|
320
|
+
it('frees the machine when the module is removed, with no manual step', async () => {
|
|
321
|
+
// The other direction: the id used to survive the module forever, so
|
|
322
|
+
// placement rejected an empty box citing a module that no longer existed
|
|
323
|
+
// and `machine remove` refused to remove it. Nothing could clear it.
|
|
324
|
+
const machine = await machineWithModuleDeployed('rpi4-lr', 'homebridge');
|
|
325
|
+
expect(getModulesOnMachine(machine.id)).toEqual(['homebridge']);
|
|
316
326
|
|
|
317
|
-
|
|
318
|
-
await assignModuleToMachine(machine.id, 'module-2');
|
|
319
|
-
await assignModuleToMachine(machine.id, 'module-3');
|
|
327
|
+
getDb().delete(modules).where(eq(modules.id, 'homebridge')).run();
|
|
320
328
|
|
|
321
|
-
|
|
322
|
-
expect(updated?.assignedModuleIds).toEqual(['module-1', 'module-2', 'module-3']);
|
|
329
|
+
expect(getModulesOnMachine(machine.id)).toEqual([]);
|
|
323
330
|
});
|
|
324
331
|
|
|
325
|
-
it('
|
|
326
|
-
await expect(assignModuleToMachine('non-existent-id', 'module-id')).rejects.toThrow(
|
|
327
|
-
/Machine not found/,
|
|
328
|
-
);
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
describe('unassignModuleFromMachine', () => {
|
|
333
|
-
it('removes module from machine', async () => {
|
|
332
|
+
it('reports an empty machine as empty', async () => {
|
|
334
333
|
const machine = await addMachine({
|
|
335
|
-
hostname: '
|
|
334
|
+
hostname: 'spare',
|
|
336
335
|
zone: 'internal',
|
|
337
|
-
ipAddress: '192.168.1.
|
|
336
|
+
ipAddress: '192.168.1.101',
|
|
338
337
|
sshUser: 'ubuntu',
|
|
339
338
|
sshKey: 'test-key',
|
|
340
|
-
hardware: { cpu_cores:
|
|
341
|
-
role: 'host',
|
|
342
|
-
interfaces: [],
|
|
343
|
-
assignedModuleIds: ['homebridge'],
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
await unassignModuleFromMachine(machine.id, 'homebridge');
|
|
347
|
-
|
|
348
|
-
const updated = await getMachine(machine.id);
|
|
349
|
-
expect(updated?.assignedModuleIds).toEqual([]);
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
it('removes specific module from multiple assignments', async () => {
|
|
353
|
-
const machine = await addMachine({
|
|
354
|
-
hostname: 'test-machine',
|
|
355
|
-
zone: 'internal',
|
|
356
|
-
ipAddress: '192.168.1.100',
|
|
357
|
-
sshUser: 'ubuntu',
|
|
358
|
-
sshKey: 'test-key',
|
|
359
|
-
hardware: { cpu_cores: 8, memory_mb: 16384, disk_gb: 256 },
|
|
339
|
+
hardware: { cpu_cores: 2, memory_mb: 2048, disk_gb: 64 },
|
|
360
340
|
role: 'host',
|
|
361
341
|
interfaces: [],
|
|
362
|
-
assignedModuleIds: ['module-1', 'module-2', 'module-3'],
|
|
363
342
|
});
|
|
364
|
-
|
|
365
|
-
await unassignModuleFromMachine(machine.id, 'module-2');
|
|
366
|
-
|
|
367
|
-
const updated = await getMachine(machine.id);
|
|
368
|
-
expect(updated?.assignedModuleIds).toEqual(['module-1', 'module-3']);
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
it('throws error for non-existent machine', async () => {
|
|
372
|
-
await expect(unassignModuleFromMachine('non-existent-id', 'module-id')).rejects.toThrow(
|
|
373
|
-
/Machine not found/,
|
|
374
|
-
);
|
|
343
|
+
expect(getModulesOnMachine(machine.id)).toEqual([]);
|
|
375
344
|
});
|
|
376
345
|
});
|
|
377
346
|
|
|
@@ -386,7 +355,6 @@ describe('machine-pool', () => {
|
|
|
386
355
|
hardware: { cpu_cores: 2, memory_mb: 2048, disk_gb: 64 },
|
|
387
356
|
role: 'host',
|
|
388
357
|
interfaces: [],
|
|
389
|
-
assignedModuleIds: [],
|
|
390
358
|
});
|
|
391
359
|
|
|
392
360
|
await removeMachine(machine.id);
|