@celilo/cli 0.4.1 → 0.5.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/api-clients/proxmox.test.ts +30 -0
- package/src/api-clients/proxmox.ts +57 -0
- package/src/cli/commands/module-show.ts +3 -3
- package/src/cli/commands/status.ts +2 -2
- package/src/hooks/capability-loader.ts +16 -5
- package/src/manifest/schema.ts +26 -15
- package/src/manifest/template-validator.test.ts +8 -8
- package/src/manifest/template-validator.ts +3 -3
- package/src/manifest/validate.test.ts +18 -17
- package/src/manifest/validate.ts +6 -6
- package/src/module/import.ts +2 -2
- package/src/services/celilo-events.test.ts +35 -5
- package/src/services/celilo-events.ts +17 -3
- package/src/services/celilo-mgmt-hooks.test.ts +14 -3
- package/src/services/deploy-preflight.ts +4 -3
- package/src/services/deployed-systems.test.ts +2 -2
- package/src/services/deployed-systems.ts +1 -1
- package/src/services/infrastructure-selector.test.ts +14 -14
- package/src/services/infrastructure-selector.ts +11 -12
- package/src/services/module-deploy.ts +4 -4
- package/src/services/restore-from-file.ts +13 -2
- package/src/services/terraform-safety.ts +8 -2
- package/src/services/zone-policy.ts +2 -2
- package/src/templates/generator.test.ts +40 -7
- package/src/templates/generator.ts +99 -10
- package/src/variables/context.test.ts +19 -42
- package/src/variables/context.ts +14 -16
- package/src/variables/lxc-nameserver.test.ts +1 -1
- package/tsconfig.json +1 -1
|
@@ -358,7 +358,7 @@ describe('Variable Context', () => {
|
|
|
358
358
|
name: 'Grafana',
|
|
359
359
|
version: '1.0.0',
|
|
360
360
|
requires: {
|
|
361
|
-
|
|
361
|
+
system: {
|
|
362
362
|
cpu: 2,
|
|
363
363
|
memory: 2048,
|
|
364
364
|
disk: 20,
|
|
@@ -372,34 +372,11 @@ describe('Variable Context', () => {
|
|
|
372
372
|
|
|
373
373
|
const context = await buildResolutionContext('grafana', db);
|
|
374
374
|
|
|
375
|
-
expect(context.selfConfig['requires.
|
|
376
|
-
expect(context.selfConfig['requires.
|
|
377
|
-
expect(context.selfConfig['requires.
|
|
378
|
-
expect(context.selfConfig['requires.
|
|
379
|
-
expect(context.selfConfig['requires.
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
test('should handle module without VM resources', async () => {
|
|
383
|
-
// Create module without VM resources
|
|
384
|
-
db.insert(modules)
|
|
385
|
-
.values({
|
|
386
|
-
id: 'simple',
|
|
387
|
-
name: 'Simple Module',
|
|
388
|
-
version: '1.0.0',
|
|
389
|
-
sourcePath: '/test/simple',
|
|
390
|
-
manifestData: {
|
|
391
|
-
id: 'simple',
|
|
392
|
-
name: 'Simple Module',
|
|
393
|
-
version: '1.0.0',
|
|
394
|
-
},
|
|
395
|
-
})
|
|
396
|
-
.run();
|
|
397
|
-
|
|
398
|
-
const context = await buildResolutionContext('simple', db);
|
|
399
|
-
|
|
400
|
-
// Should not have requires.machine keys
|
|
401
|
-
expect(context.selfConfig['requires.machine.cpu']).toBeUndefined();
|
|
402
|
-
expect(context.selfConfig['requires.machine.memory']).toBeUndefined();
|
|
375
|
+
expect(context.selfConfig['requires.system.cpu']).toBe('2');
|
|
376
|
+
expect(context.selfConfig['requires.system.memory']).toBe('2048');
|
|
377
|
+
expect(context.selfConfig['requires.system.disk']).toBe('20');
|
|
378
|
+
expect(context.selfConfig['requires.system.storage']).toBe('local-lvm');
|
|
379
|
+
expect(context.selfConfig['requires.system.zone']).toBe('app');
|
|
403
380
|
});
|
|
404
381
|
|
|
405
382
|
test('should auto-derive inventory variables from target_ip', async () => {
|
|
@@ -472,7 +449,7 @@ describe('Variable Context', () => {
|
|
|
472
449
|
],
|
|
473
450
|
},
|
|
474
451
|
requires: {
|
|
475
|
-
|
|
452
|
+
system: {
|
|
476
453
|
zone: 'dmz',
|
|
477
454
|
},
|
|
478
455
|
},
|
|
@@ -529,7 +506,7 @@ describe('Variable Context', () => {
|
|
|
529
506
|
{ name: 'target_ip', type: 'string', required: true, source: 'user' },
|
|
530
507
|
],
|
|
531
508
|
},
|
|
532
|
-
requires: {
|
|
509
|
+
requires: { system: { zone: 'dmz' } },
|
|
533
510
|
},
|
|
534
511
|
})
|
|
535
512
|
.run();
|
|
@@ -649,7 +626,7 @@ describe('Variable Context', () => {
|
|
|
649
626
|
{ name: 'target_ip', type: 'string', required: true, source: 'user' },
|
|
650
627
|
],
|
|
651
628
|
},
|
|
652
|
-
requires: {
|
|
629
|
+
requires: { system: { zone: 'dmz' } },
|
|
653
630
|
},
|
|
654
631
|
})
|
|
655
632
|
.run();
|
|
@@ -672,7 +649,7 @@ describe('Variable Context', () => {
|
|
|
672
649
|
{ name: 'target_ip', type: 'string', required: true, source: 'user' },
|
|
673
650
|
],
|
|
674
651
|
},
|
|
675
|
-
requires: {
|
|
652
|
+
requires: { system: { zone: 'dmz' } },
|
|
676
653
|
},
|
|
677
654
|
})
|
|
678
655
|
.run();
|
|
@@ -890,7 +867,7 @@ describe('Variable Context', () => {
|
|
|
890
867
|
name: 'App With Resources',
|
|
891
868
|
version: '1.0.0',
|
|
892
869
|
requires: {
|
|
893
|
-
|
|
870
|
+
system: {
|
|
894
871
|
cpu: 2,
|
|
895
872
|
memory: 2048,
|
|
896
873
|
disk: 20,
|
|
@@ -935,7 +912,7 @@ describe('Variable Context', () => {
|
|
|
935
912
|
name: 'Custom Resources',
|
|
936
913
|
version: '1.0.0',
|
|
937
914
|
requires: {
|
|
938
|
-
|
|
915
|
+
system: {
|
|
939
916
|
cpu: 2,
|
|
940
917
|
memory: 2048,
|
|
941
918
|
},
|
|
@@ -997,7 +974,7 @@ describe('Variable Context', () => {
|
|
|
997
974
|
name: 'Partial Resources',
|
|
998
975
|
version: '1.0.0',
|
|
999
976
|
requires: {
|
|
1000
|
-
|
|
977
|
+
system: {
|
|
1001
978
|
cpu: 1,
|
|
1002
979
|
memory: 512,
|
|
1003
980
|
// No disk or storage specified
|
|
@@ -1031,7 +1008,7 @@ describe('Variable Context', () => {
|
|
|
1031
1008
|
name: 'DMZ Module',
|
|
1032
1009
|
version: '1.0.0',
|
|
1033
1010
|
requires: {
|
|
1034
|
-
|
|
1011
|
+
system: {
|
|
1035
1012
|
zone: 'dmz',
|
|
1036
1013
|
},
|
|
1037
1014
|
},
|
|
@@ -1073,7 +1050,7 @@ describe('Variable Context', () => {
|
|
|
1073
1050
|
name: 'App Module',
|
|
1074
1051
|
version: '1.0.0',
|
|
1075
1052
|
requires: {
|
|
1076
|
-
|
|
1053
|
+
system: {
|
|
1077
1054
|
zone: 'app',
|
|
1078
1055
|
},
|
|
1079
1056
|
},
|
|
@@ -1103,7 +1080,7 @@ describe('Variable Context', () => {
|
|
|
1103
1080
|
name: 'Secure Module',
|
|
1104
1081
|
version: '1.0.0',
|
|
1105
1082
|
requires: {
|
|
1106
|
-
|
|
1083
|
+
system: {
|
|
1107
1084
|
zone: 'secure',
|
|
1108
1085
|
},
|
|
1109
1086
|
},
|
|
@@ -1158,7 +1135,7 @@ describe('Variable Context', () => {
|
|
|
1158
1135
|
name: 'Custom Network',
|
|
1159
1136
|
version: '1.0.0',
|
|
1160
1137
|
requires: {
|
|
1161
|
-
|
|
1138
|
+
system: {
|
|
1162
1139
|
zone: 'dmz',
|
|
1163
1140
|
},
|
|
1164
1141
|
},
|
|
@@ -1198,7 +1175,7 @@ describe('Variable Context', () => {
|
|
|
1198
1175
|
name: 'VPS External',
|
|
1199
1176
|
version: '1.0.0',
|
|
1200
1177
|
requires: {
|
|
1201
|
-
|
|
1178
|
+
system: {
|
|
1202
1179
|
zone: 'external',
|
|
1203
1180
|
},
|
|
1204
1181
|
},
|
|
@@ -1228,7 +1205,7 @@ describe('Variable Context', () => {
|
|
|
1228
1205
|
name: 'No Net Config',
|
|
1229
1206
|
version: '1.0.0',
|
|
1230
1207
|
requires: {
|
|
1231
|
-
|
|
1208
|
+
system: {
|
|
1232
1209
|
zone: 'dmz',
|
|
1233
1210
|
},
|
|
1234
1211
|
},
|
package/src/variables/context.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
systemSecrets,
|
|
15
15
|
} from '../db/schema';
|
|
16
16
|
import { allocateResources, getAllocation } from '../ipam/allocator';
|
|
17
|
-
import { type ModuleManifest, getDeclaredSystems } from '../manifest/schema';
|
|
17
|
+
import { type ModuleManifest, getDeclaredSystems, getSingularSystemSpec } from '../manifest/schema';
|
|
18
18
|
import { decryptSecret } from '../secrets/encryption';
|
|
19
19
|
import { getOrCreateMasterKey } from '../secrets/master-key';
|
|
20
20
|
import { upsertModuleConfig } from '../services/module-config';
|
|
@@ -76,7 +76,7 @@ async function autoAssignFromWellKnown(
|
|
|
76
76
|
const wellKnown = getWellKnownCapability(capability.name);
|
|
77
77
|
|
|
78
78
|
// Determine current zone (from config or manifest)
|
|
79
|
-
const currentZone = selfConfig.zone || manifest
|
|
79
|
+
const currentZone = selfConfig.zone || getSingularSystemSpec(manifest)?.zone;
|
|
80
80
|
|
|
81
81
|
// Auto-assign hostname if not already set
|
|
82
82
|
if (!selfConfig.hostname) {
|
|
@@ -261,22 +261,22 @@ export async function buildResolutionContext(
|
|
|
261
261
|
// Auto-apply VM resource defaults from manifest if not already configured
|
|
262
262
|
if (module?.manifestData) {
|
|
263
263
|
const manifest = module.manifestData as ModuleManifest;
|
|
264
|
-
const
|
|
264
|
+
const systemResources = getSingularSystemSpec(manifest);
|
|
265
265
|
|
|
266
|
-
if (
|
|
266
|
+
if (systemResources) {
|
|
267
267
|
// Map manifest fields to module variable names and apply defaults
|
|
268
268
|
const resourceMappings: Array<{
|
|
269
|
-
manifestKey: keyof typeof
|
|
269
|
+
manifestKey: keyof typeof systemResources;
|
|
270
270
|
configKey: string;
|
|
271
271
|
}> = [
|
|
272
|
-
{ manifestKey: 'cpu', configKey: 'cores' }, // manifest.requires.
|
|
272
|
+
{ manifestKey: 'cpu', configKey: 'cores' }, // manifest.requires.system.cpu → cores variable
|
|
273
273
|
{ manifestKey: 'memory', configKey: 'memory' },
|
|
274
274
|
{ manifestKey: 'disk', configKey: 'disk' },
|
|
275
275
|
{ manifestKey: 'storage', configKey: 'storage' },
|
|
276
276
|
];
|
|
277
277
|
|
|
278
278
|
for (const { manifestKey, configKey } of resourceMappings) {
|
|
279
|
-
const value =
|
|
279
|
+
const value = systemResources[manifestKey];
|
|
280
280
|
|
|
281
281
|
// Manifest fields are typed (cpu: number, storage: string, etc.).
|
|
282
282
|
// Pass them through unstringified so valueJson preserves the
|
|
@@ -374,15 +374,13 @@ export async function buildResolutionContext(
|
|
|
374
374
|
}
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
-
// Add
|
|
378
|
-
// reference them via $self:requires.
|
|
377
|
+
// Add system requirements from manifest to selfConfig so templates can
|
|
378
|
+
// reference them via $self:requires.system.<field>.
|
|
379
379
|
if (module?.manifestData) {
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
for (const [key, value] of Object.entries(machineRequires)) {
|
|
385
|
-
selfConfig[`requires.machine.${key}`] = String(value);
|
|
380
|
+
const systemSpec = getSingularSystemSpec(module.manifestData as ModuleManifest);
|
|
381
|
+
if (systemSpec) {
|
|
382
|
+
for (const [key, value] of Object.entries(systemSpec)) {
|
|
383
|
+
selfConfig[`requires.system.${key}`] = String(value);
|
|
386
384
|
}
|
|
387
385
|
}
|
|
388
386
|
}
|
|
@@ -550,7 +548,7 @@ export async function buildResolutionContext(
|
|
|
550
548
|
// Auto-derive network config from zone (gateway, vlan, subnet, bridge)
|
|
551
549
|
if (module?.manifestData) {
|
|
552
550
|
const manifest = module.manifestData as ModuleManifest;
|
|
553
|
-
const zone = selfConfig.zone || manifest
|
|
551
|
+
const zone = selfConfig.zone || getSingularSystemSpec(manifest)?.zone;
|
|
554
552
|
|
|
555
553
|
// If zone from manifest but not in selfConfig, store it as first-class config
|
|
556
554
|
if (zone && !selfConfig.zone) {
|
|
@@ -24,7 +24,7 @@ describe('lxc_nameserver composition', () => {
|
|
|
24
24
|
id: 'consumer',
|
|
25
25
|
name: 'consumer',
|
|
26
26
|
version: '1.0.0',
|
|
27
|
-
manifestData: { requires: {
|
|
27
|
+
manifestData: { requires: { system: { zone: 'app' } } },
|
|
28
28
|
sourcePath: '/tmp/consumer',
|
|
29
29
|
})
|
|
30
30
|
.run();
|