@celilo/cli 0.3.30-alpha.0 → 0.4.0-alpha.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/drizzle/0005_module_operations.sql +12 -0
- package/drizzle/0006_base_module_aspects.sql +15 -0
- package/drizzle/0007_module_systems.sql +17 -0
- package/drizzle/meta/_journal.json +21 -0
- package/package.json +3 -3
- package/schemas/system_config.json +14 -28
- package/src/ansible/inventory.test.ts +46 -62
- package/src/ansible/inventory.ts +48 -25
- package/src/capabilities/registration.ts +25 -7
- package/src/capabilities/validation.test.ts +30 -0
- package/src/capabilities/validation.ts +8 -0
- package/src/cli/backup-rename.test.ts +95 -0
- package/src/cli/cli.test.ts +17 -23
- package/src/cli/command-registry.ts +199 -0
- package/src/cli/commands/backup-list.ts +1 -1
- package/src/cli/commands/events.ts +96 -0
- package/src/cli/commands/machine-add.ts +103 -59
- package/src/cli/commands/module-import.ts +153 -4
- package/src/cli/commands/module-remove.ts +86 -17
- package/src/cli/commands/module-status.ts +6 -2
- package/src/cli/commands/publish/alpha.test.ts +185 -0
- package/src/cli/commands/publish/alpha.ts +226 -0
- package/src/cli/commands/publish/changesets.test.ts +89 -0
- package/src/cli/commands/publish/changesets.ts +144 -0
- package/src/cli/commands/publish/consumer-pins.test.ts +155 -0
- package/src/cli/commands/publish/consumer-pins.ts +149 -0
- package/src/cli/commands/publish/execute.ts +131 -0
- package/src/cli/commands/publish/global-install.test.ts +154 -0
- package/src/cli/commands/publish/global-install.ts +171 -0
- package/src/cli/commands/publish/helpers.ts +227 -0
- package/src/cli/commands/publish/index.ts +365 -0
- package/src/cli/commands/publish/module-registry.test.ts +40 -0
- package/src/cli/commands/publish/module-registry.ts +64 -0
- package/src/cli/commands/publish/plan.ts +107 -0
- package/src/cli/commands/publish/preflight.ts +238 -0
- package/src/cli/commands/publish/types.ts +264 -0
- package/src/cli/commands/publish/workspace.test.ts +323 -0
- package/src/cli/commands/publish/workspace.ts +596 -0
- package/src/cli/commands/restore.ts +126 -0
- package/src/cli/commands/storage-add-local.ts +1 -1
- package/src/cli/commands/storage-add-s3.ts +1 -1
- package/src/cli/commands/subscribers-add.ts +68 -0
- package/src/cli/commands/subscribers-list.ts +48 -0
- package/src/cli/commands/subscribers-remove.ts +38 -0
- package/src/cli/commands/subscribers-serve.ts +77 -0
- package/src/cli/commands/subscribers-status.ts +33 -0
- package/src/cli/commands/subscribers-test.ts +71 -0
- package/src/cli/commands/system-apply-config-equivalence.test.ts +108 -0
- package/src/cli/commands/system-apply-config.test.ts +70 -0
- package/src/cli/commands/system-apply-config.ts +130 -0
- package/src/cli/commands/system-audit.ts +2 -1
- package/src/cli/commands/system-init-deprecation.test.ts +90 -0
- package/src/cli/commands/system-init.ts +36 -70
- package/src/cli/commands/system-update.ts +3 -2
- package/src/cli/completion.ts +22 -1
- package/src/cli/index.ts +214 -6
- package/src/cli/interactive-config.test.ts +19 -0
- package/src/cli/restore-command.test.ts +131 -0
- package/src/db/client.ts +42 -0
- package/src/db/schema.test.ts +13 -16
- package/src/db/schema.ts +161 -9
- package/src/hooks/capability-loader-firewall.test.ts +6 -15
- package/src/hooks/capability-loader.test.ts +2 -3
- package/src/hooks/capability-loader.ts +36 -2
- package/src/hooks/define-hook.test.ts +4 -0
- package/src/hooks/executor.test.ts +18 -0
- package/src/hooks/executor.ts +21 -2
- package/src/hooks/load-hook-config.test.ts +26 -24
- package/src/hooks/load-hook-config.ts +11 -2
- package/src/hooks/run-named-hook.ts +16 -0
- package/src/hooks/types.ts +9 -1
- package/src/manifest/contracts/v1.ts +70 -0
- package/src/manifest/schema.ts +262 -16
- package/src/manifest/validate-privileged.test.ts +84 -0
- package/src/manifest/validate.test.ts +156 -0
- package/src/manifest/validate.ts +69 -0
- package/src/module/import.ts +12 -0
- package/src/services/aspect-approvals.test.ts +231 -0
- package/src/services/aspect-approvals.ts +120 -0
- package/src/services/aspect-runner.test.ts +493 -0
- package/src/services/aspect-runner.ts +438 -0
- package/src/services/aspect-template-resolver.test.ts +101 -0
- package/src/services/aspect-template-resolver.ts +122 -0
- package/src/services/backup-create.ts +104 -25
- package/src/services/backup-envelope-roundtrip.test.ts +199 -0
- package/src/services/backup-in-flight-refusal.test.ts +163 -0
- package/src/services/backup-manifest.test.ts +115 -0
- package/src/services/backup-manifest.ts +163 -0
- package/src/services/backup-restore.ts +154 -19
- package/src/services/build-bus/delivery-events.ts +92 -0
- package/src/services/build-bus/event-factory.ts +54 -0
- package/src/services/build-bus/fan-out.test.ts +279 -0
- package/src/services/build-bus/fan-out.ts +161 -0
- package/src/services/build-bus/hook-dispatch-mgmt.test.ts +157 -0
- package/src/services/build-bus/hook-dispatch.test.ts +207 -0
- package/src/services/build-bus/hook-dispatch.ts +198 -0
- package/src/services/build-bus/hook-dispatcher.ts +115 -0
- package/src/services/build-bus/index.ts +41 -0
- package/src/services/build-bus/receiver-server.test.ts +179 -0
- package/src/services/build-bus/receiver-server.ts +159 -0
- package/src/services/build-bus/status.test.ts +212 -0
- package/src/services/build-bus/status.ts +213 -0
- package/src/services/build-bus/subscriber-store.ts +113 -0
- package/src/services/celilo-events.test.ts +70 -0
- package/src/services/celilo-events.ts +92 -0
- package/src/services/celilo-mgmt-hooks.test.ts +296 -0
- package/src/services/config-interview.ts +13 -95
- package/src/services/cross-module-data-manager.ts +2 -31
- package/src/services/cross-module-read.test.ts +250 -0
- package/src/services/cross-module-read.ts +232 -0
- package/src/services/deploy-validation.ts +7 -0
- package/src/services/deployed-systems.test.ts +235 -0
- package/src/services/deployed-systems.ts +308 -0
- package/src/services/dns-provider-backfill.ts +75 -0
- package/src/services/health-runner.ts +19 -3
- package/src/services/infrastructure-variable-resolver.test.ts +6 -32
- package/src/services/infrastructure-variable-resolver.ts +3 -13
- package/src/services/machine-detector.ts +104 -48
- package/src/services/machine-pool.ts +145 -2
- package/src/services/module-config.ts +78 -120
- package/src/services/module-deploy.ts +113 -40
- package/src/services/module-operations.test.ts +154 -0
- package/src/services/module-operations.ts +154 -0
- package/src/services/module-subscriptions.test.ts +58 -0
- package/src/services/module-subscriptions.ts +24 -1
- package/src/services/module-types-generator.test.ts +3 -3
- package/src/services/module-types-generator.ts +7 -2
- package/src/services/proxmox-reconcile.test.ts +333 -0
- package/src/services/proxmox-reconcile.ts +156 -0
- package/src/services/proxmox-state-recovery.ts +3 -24
- package/src/services/restore-from-file.test.ts +177 -0
- package/src/services/restore-from-file.ts +355 -0
- package/src/services/restore-preflight.test.ts +127 -0
- package/src/services/restore-preflight.ts +118 -0
- package/src/services/storage-providers/s3.ts +10 -2
- package/src/services/system-identity.ts +30 -0
- package/src/services/system-init.test.ts +64 -21
- package/src/services/system-init.ts +28 -26
- package/src/templates/generator.test.ts +7 -16
- package/src/templates/generator.ts +28 -115
- package/src/test-utils/integration.ts +5 -2
- package/src/types/infrastructure.ts +8 -0
- package/src/variables/computed/computed-integration.test.ts +191 -0
- package/src/variables/computed/computed.test.ts +177 -0
- package/src/variables/computed/evaluate.ts +271 -0
- package/src/variables/computed/marker.ts +53 -0
- package/src/variables/computed/parse.ts +262 -0
- package/src/variables/computed/provider-lookup.ts +130 -0
- package/src/variables/context.test.ts +89 -28
- package/src/variables/context.ts +196 -191
- package/src/variables/parser.ts +3 -3
- package/src/variables/resolver.test.ts +61 -0
- package/src/variables/resolver.ts +81 -0
- package/src/variables/types.ts +23 -1
- package/src/services/dns-auto-register.ts +0 -211
|
@@ -8,7 +8,7 @@ import type { DbClient } from '../db/client';
|
|
|
8
8
|
import { systemConfig } from '../db/schema';
|
|
9
9
|
import { setupTestDatabase } from '../test-utils/database';
|
|
10
10
|
import {
|
|
11
|
-
|
|
11
|
+
autoDetectSSHKeys,
|
|
12
12
|
getDefaultConfiguration,
|
|
13
13
|
initializeSystem,
|
|
14
14
|
isSystemInitialized,
|
|
@@ -22,25 +22,35 @@ describe('System Init Service', () => {
|
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
describe('getDefaultConfiguration', () => {
|
|
25
|
-
test('returns
|
|
26
|
-
const defaults =
|
|
25
|
+
test('returns default values from schema', () => {
|
|
26
|
+
const defaults = getDefaultConfiguration();
|
|
27
27
|
|
|
28
|
+
// network.bridge keeps its default (a container-service concept, not
|
|
29
|
+
// a zone address).
|
|
28
30
|
expect(defaults['network.bridge']).toBe('vmbr0');
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
// Network/DNS addressing is no longer defaulted
|
|
32
|
+
// (v2/NETWORK_CONFIG_TO_FIREWALL.md): internal is discovered at
|
|
33
|
+
// celilo-mgmt install; dmz/app/secure appear only when a firewall
|
|
34
|
+
// module provides them.
|
|
35
|
+
expect(defaults['network.dmz.subnet']).toBeUndefined();
|
|
36
|
+
expect(defaults['network.dmz.vlan']).toBeUndefined();
|
|
37
|
+
expect(defaults['network.internal.subnet']).toBeUndefined();
|
|
38
|
+
expect(defaults['dns.primary']).toBeUndefined();
|
|
39
|
+
expect(defaults['dns.fallback']).toBeUndefined();
|
|
31
40
|
// primary_domain was removed from system config in MANIFEST_V2 D9.
|
|
32
41
|
expect(defaults.primary_domain).toBeUndefined();
|
|
33
42
|
});
|
|
34
43
|
});
|
|
35
44
|
|
|
36
|
-
describe('
|
|
37
|
-
test('returns
|
|
38
|
-
const
|
|
45
|
+
describe('autoDetectSSHKeys', () => {
|
|
46
|
+
test('returns an array of detected keys', () => {
|
|
47
|
+
const keys = autoDetectSSHKeys();
|
|
39
48
|
|
|
40
|
-
// Either
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
expect(key.
|
|
49
|
+
// Either empty (no key found) or entries with non-empty content.
|
|
50
|
+
expect(Array.isArray(keys)).toBe(true);
|
|
51
|
+
for (const key of keys) {
|
|
52
|
+
expect(typeof key.content).toBe('string');
|
|
53
|
+
expect(key.content.length).toBeGreaterThan(0);
|
|
44
54
|
}
|
|
45
55
|
});
|
|
46
56
|
});
|
|
@@ -62,8 +72,14 @@ describe('System Init Service', () => {
|
|
|
62
72
|
expect(networkBridge?.value).toBe('vmbr0');
|
|
63
73
|
});
|
|
64
74
|
|
|
65
|
-
test('computes gateway IPs from subnets', () => {
|
|
66
|
-
|
|
75
|
+
test('computes gateway IPs from provided subnets', () => {
|
|
76
|
+
// Subnets are no longer defaulted, so the operator/firewall supplies
|
|
77
|
+
// them as overrides; the gateway is still auto-computed from the
|
|
78
|
+
// subnet network address.
|
|
79
|
+
initializeSystem(db, {
|
|
80
|
+
'network.dmz.subnet': '10.0.10.0/24',
|
|
81
|
+
'network.app.subnet': '10.0.20.0/24',
|
|
82
|
+
});
|
|
67
83
|
|
|
68
84
|
// Check DMZ gateway was computed
|
|
69
85
|
const dmzGateway = db
|
|
@@ -84,6 +100,30 @@ describe('System Init Service', () => {
|
|
|
84
100
|
expect(appGateway?.value).toBe('10.0.20.1');
|
|
85
101
|
});
|
|
86
102
|
|
|
103
|
+
test('does not seed network/DNS addressing by default', () => {
|
|
104
|
+
initializeSystem(db);
|
|
105
|
+
|
|
106
|
+
for (const key of [
|
|
107
|
+
'network.dmz.subnet',
|
|
108
|
+
'network.app.subnet',
|
|
109
|
+
'network.secure.subnet',
|
|
110
|
+
'network.internal.subnet',
|
|
111
|
+
'dns.primary',
|
|
112
|
+
'dns.fallback',
|
|
113
|
+
]) {
|
|
114
|
+
const row = db.select().from(systemConfig).where(eq(systemConfig.key, key)).get();
|
|
115
|
+
expect(row).toBeUndefined();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// network.bridge IS still seeded.
|
|
119
|
+
const bridge = db
|
|
120
|
+
.select()
|
|
121
|
+
.from(systemConfig)
|
|
122
|
+
.where(eq(systemConfig.key, 'network.bridge'))
|
|
123
|
+
.get();
|
|
124
|
+
expect(bridge?.value).toBe('vmbr0');
|
|
125
|
+
});
|
|
126
|
+
|
|
87
127
|
test('applies overrides instead of defaults', () => {
|
|
88
128
|
initializeSystem(db, {
|
|
89
129
|
primary_domain: 'custom.com',
|
|
@@ -112,8 +152,9 @@ describe('System Init Service', () => {
|
|
|
112
152
|
test('includes SSH key if auto-detected', () => {
|
|
113
153
|
const config = initializeSystem(db);
|
|
114
154
|
|
|
115
|
-
const
|
|
116
|
-
if (
|
|
155
|
+
const detectedKeys = autoDetectSSHKeys();
|
|
156
|
+
if (detectedKeys.length > 0) {
|
|
157
|
+
const detectedKey = detectedKeys[0].content;
|
|
117
158
|
expect(config['ssh.public_key']).toBe(detectedKey);
|
|
118
159
|
|
|
119
160
|
const sshKey = db
|
|
@@ -140,14 +181,16 @@ describe('System Init Service', () => {
|
|
|
140
181
|
expect(initialized).toBe(true);
|
|
141
182
|
});
|
|
142
183
|
|
|
143
|
-
test('
|
|
144
|
-
//
|
|
145
|
-
//
|
|
184
|
+
test('keys off the sentinel, not config rows', async () => {
|
|
185
|
+
// Config rows present but no sentinel → NOT initialized. Initialization
|
|
186
|
+
// is tracked by the explicit `system.initialized` marker
|
|
187
|
+
// (v2/NETWORK_CONFIG_TO_FIREWALL.md), not by any network row.
|
|
146
188
|
db.insert(systemConfig).values({ key: 'network.dmz.subnet', value: '10.0.0.0/24' }).run();
|
|
147
189
|
db.insert(systemConfig).values({ key: 'ssh.public_key', value: 'ssh-rsa AAAA...' }).run();
|
|
190
|
+
expect(isSystemInitialized(db)).toBe(false);
|
|
148
191
|
|
|
149
|
-
|
|
150
|
-
expect(
|
|
192
|
+
db.insert(systemConfig).values({ key: 'system.initialized', value: 'true' }).run();
|
|
193
|
+
expect(isSystemInitialized(db)).toBe(true);
|
|
151
194
|
});
|
|
152
195
|
});
|
|
153
196
|
});
|
|
@@ -127,12 +127,6 @@ export function autoDetectSSHKeys(): DetectedSSHKey[] {
|
|
|
127
127
|
return keys;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
/** @deprecated Use autoDetectSSHKeys() instead */
|
|
131
|
-
export function autoDetectSSHKey(): string | null {
|
|
132
|
-
const keys = autoDetectSSHKeys();
|
|
133
|
-
return keys.length > 0 ? keys[0].content : null;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
130
|
/**
|
|
137
131
|
* Initialize system configuration with defaults
|
|
138
132
|
*
|
|
@@ -165,9 +159,9 @@ export function initializeSystem(
|
|
|
165
159
|
|
|
166
160
|
// Auto-detect SSH key if not provided
|
|
167
161
|
if (!config['ssh.public_key']) {
|
|
168
|
-
const
|
|
169
|
-
if (
|
|
170
|
-
config['ssh.public_key'] =
|
|
162
|
+
const detectedKeys = autoDetectSSHKeys();
|
|
163
|
+
if (detectedKeys.length > 0) {
|
|
164
|
+
config['ssh.public_key'] = detectedKeys[0].content;
|
|
171
165
|
}
|
|
172
166
|
}
|
|
173
167
|
|
|
@@ -204,6 +198,19 @@ export function initializeSystem(
|
|
|
204
198
|
.run();
|
|
205
199
|
}
|
|
206
200
|
|
|
201
|
+
// Explicit init marker. Network/DNS addressing is no longer seeded with
|
|
202
|
+
// defaults (v2/NETWORK_CONFIG_TO_FIREWALL.md), so we can't key
|
|
203
|
+
// "is this system initialized?" off a network row anymore. Write a
|
|
204
|
+
// dedicated sentinel instead. additionalProperties:true in the schema
|
|
205
|
+
// permits this non-schema key.
|
|
206
|
+
db.insert(systemConfig)
|
|
207
|
+
.values({ key: 'system.initialized', value: 'true' })
|
|
208
|
+
.onConflictDoUpdate({
|
|
209
|
+
target: systemConfig.key,
|
|
210
|
+
set: { value: 'true' },
|
|
211
|
+
})
|
|
212
|
+
.run();
|
|
213
|
+
|
|
207
214
|
return config;
|
|
208
215
|
}
|
|
209
216
|
|
|
@@ -228,26 +235,21 @@ export function loadExistingConfiguration(db: DbClient): Record<string, string>
|
|
|
228
235
|
/**
|
|
229
236
|
* Check if system is already initialized
|
|
230
237
|
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
238
|
+
* Keys off the explicit `system.initialized` sentinel written by
|
|
239
|
+
* initializeSystem(). Earlier this checked for a seeded network zone
|
|
240
|
+
* subnet, but network/DNS addressing is no longer defaulted
|
|
241
|
+
* (v2/NETWORK_CONFIG_TO_FIREWALL.md), so a fresh init leaves those
|
|
242
|
+
* rows absent.
|
|
234
243
|
*
|
|
235
244
|
* @param db - Database instance
|
|
236
|
-
* @returns true if
|
|
245
|
+
* @returns true if initializeSystem has run against this database
|
|
237
246
|
*/
|
|
238
247
|
export function isSystemInitialized(db: DbClient): boolean {
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const result = db.select().from(systemConfig).where(eq(systemConfig.key, key)).get();
|
|
245
|
-
|
|
246
|
-
if (result) {
|
|
247
|
-
foundKeys++;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
248
|
+
const result = db
|
|
249
|
+
.select()
|
|
250
|
+
.from(systemConfig)
|
|
251
|
+
.where(eq(systemConfig.key, 'system.initialized'))
|
|
252
|
+
.get();
|
|
250
253
|
|
|
251
|
-
|
|
252
|
-
return foundKeys >= 2;
|
|
254
|
+
return result?.value === 'true';
|
|
253
255
|
}
|
|
@@ -3,7 +3,8 @@ import { existsSync } from 'node:fs';
|
|
|
3
3
|
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { type DbClient, createDbClient } from '../db/client';
|
|
6
|
-
import { capabilities
|
|
6
|
+
import { capabilities } from '../db/schema';
|
|
7
|
+
import { upsertModuleConfig } from '../services/module-config';
|
|
7
8
|
import {
|
|
8
9
|
discoverTemplateFiles,
|
|
9
10
|
generateTemplates,
|
|
@@ -319,12 +320,8 @@ describe('Template Generator', () => {
|
|
|
319
320
|
`INSERT INTO modules (id, name, version, source_path, manifest_data) VALUES ('test-module', 'Test', '1.0.0', '/path', '{}')`,
|
|
320
321
|
);
|
|
321
322
|
|
|
322
|
-
db.
|
|
323
|
-
|
|
324
|
-
{ moduleId: 'test-module', key: 'target_ip', value: '192.168.0.50' },
|
|
325
|
-
{ moduleId: 'test-module', key: 'hostname', value: 'test' },
|
|
326
|
-
])
|
|
327
|
-
.run();
|
|
323
|
+
upsertModuleConfig(db, 'test-module', 'target_ip', '192.168.0.50');
|
|
324
|
+
upsertModuleConfig(db, 'test-module', 'hostname', 'test');
|
|
328
325
|
|
|
329
326
|
// Insert system config
|
|
330
327
|
db.$client.run(
|
|
@@ -375,9 +372,7 @@ resource "proxmox_lxc" "container" {
|
|
|
375
372
|
db.$client.run(
|
|
376
373
|
`INSERT INTO modules (id, name, version, source_path, manifest_data) VALUES ('test-module', 'Test', '1.0.0', '/path', '{}')`,
|
|
377
374
|
);
|
|
378
|
-
db
|
|
379
|
-
.values({ moduleId: 'test-module', key: 'name', value: 'test' })
|
|
380
|
-
.run();
|
|
375
|
+
upsertModuleConfig(db, 'test-module', 'name', 'test');
|
|
381
376
|
|
|
382
377
|
// Create templates
|
|
383
378
|
await mkdir(join(TEST_MODULE_DIR, 'terraform'), { recursive: true });
|
|
@@ -511,9 +506,7 @@ resource "proxmox_lxc" "container" {
|
|
|
511
506
|
`INSERT INTO modules (id, name, version, source_path, manifest_data) VALUES ('test-module', 'Test', '1.0.0', '/path', ?)`,
|
|
512
507
|
[manifestWithResources],
|
|
513
508
|
);
|
|
514
|
-
db
|
|
515
|
-
.values({ moduleId: 'test-module', key: 'name', value: 'test' })
|
|
516
|
-
.run();
|
|
509
|
+
upsertModuleConfig(db, 'test-module', 'name', 'test');
|
|
517
510
|
|
|
518
511
|
// Insert dummy machine (required for foreign key)
|
|
519
512
|
db.$client.run(
|
|
@@ -598,9 +591,7 @@ resource "proxmox_lxc" "container" {
|
|
|
598
591
|
`INSERT INTO modules (id, name, version, source_path, manifest_data) VALUES ('test-module', 'Test', '1.0.0', '/path', ?)`,
|
|
599
592
|
[manifestWithResources],
|
|
600
593
|
);
|
|
601
|
-
db
|
|
602
|
-
.values({ moduleId: 'test-module', key: 'name', value: 'test' })
|
|
603
|
-
.run();
|
|
594
|
+
upsertModuleConfig(db, 'test-module', 'name', 'test');
|
|
604
595
|
|
|
605
596
|
// Insert dummy container service (required for foreign key)
|
|
606
597
|
db.$client.run(
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
import type { AnsibleCollection, ModuleManifest } from '../manifest/schema';
|
|
19
19
|
import { validateZoneRequirements } from '../manifest/validate';
|
|
20
20
|
import { selectInfrastructure } from '../services/infrastructure-selector';
|
|
21
|
+
import { upsertModuleConfig } from '../services/module-config';
|
|
21
22
|
import type { InfrastructureSelection } from '../types/infrastructure';
|
|
22
23
|
import { convertSecretsToJinja } from '../variables/ansible-resolver';
|
|
23
24
|
import { buildResolutionContext } from '../variables/context';
|
|
@@ -532,39 +533,11 @@ export async function generateTemplates(options: GenerateOptions): Promise<Gener
|
|
|
532
533
|
);
|
|
533
534
|
}
|
|
534
535
|
|
|
535
|
-
// Store allocation in config temporarily (will be available in context)
|
|
536
|
-
// These are "virtual" config values
|
|
537
|
-
//
|
|
538
|
-
db.
|
|
539
|
-
|
|
540
|
-
moduleId,
|
|
541
|
-
key: '__ipam_vmid',
|
|
542
|
-
value: String(allocation.vmid),
|
|
543
|
-
valueJson: null,
|
|
544
|
-
})
|
|
545
|
-
.onConflictDoUpdate({
|
|
546
|
-
target: [moduleConfigs.moduleId, moduleConfigs.key],
|
|
547
|
-
set: {
|
|
548
|
-
value: String(allocation.vmid),
|
|
549
|
-
},
|
|
550
|
-
})
|
|
551
|
-
.run();
|
|
552
|
-
|
|
553
|
-
// Insert container IP
|
|
554
|
-
db.insert(moduleConfigs)
|
|
555
|
-
.values({
|
|
556
|
-
moduleId,
|
|
557
|
-
key: '__ipam_container_ip',
|
|
558
|
-
value: allocation.containerIp,
|
|
559
|
-
valueJson: null,
|
|
560
|
-
})
|
|
561
|
-
.onConflictDoUpdate({
|
|
562
|
-
target: [moduleConfigs.moduleId, moduleConfigs.key],
|
|
563
|
-
set: {
|
|
564
|
-
value: allocation.containerIp,
|
|
565
|
-
},
|
|
566
|
-
})
|
|
567
|
-
.run();
|
|
536
|
+
// Store allocation in config temporarily (will be available in context).
|
|
537
|
+
// These are "virtual" config values (the __ipam_ prefix flags them as
|
|
538
|
+
// framework-internal). Typed: vmid is number, container_ip is string.
|
|
539
|
+
upsertModuleConfig(db, moduleId, '__ipam_vmid', allocation.vmid);
|
|
540
|
+
upsertModuleConfig(db, moduleId, '__ipam_container_ip', allocation.containerIp);
|
|
568
541
|
} catch (error) {
|
|
569
542
|
return {
|
|
570
543
|
success: false,
|
|
@@ -598,18 +571,7 @@ export async function generateTemplates(options: GenerateOptions): Promise<Gener
|
|
|
598
571
|
];
|
|
599
572
|
|
|
600
573
|
for (const prop of infraProperties) {
|
|
601
|
-
db.
|
|
602
|
-
.values({
|
|
603
|
-
moduleId,
|
|
604
|
-
key: `__infra_${prop.key}`,
|
|
605
|
-
value: prop.value,
|
|
606
|
-
valueJson: null,
|
|
607
|
-
})
|
|
608
|
-
.onConflictDoUpdate({
|
|
609
|
-
target: [moduleConfigs.moduleId, moduleConfigs.key],
|
|
610
|
-
set: { value: prop.value, updatedAt: new Date() },
|
|
611
|
-
})
|
|
612
|
-
.run();
|
|
574
|
+
upsertModuleConfig(db, moduleId, `__infra_${prop.key}`, prop.value);
|
|
613
575
|
}
|
|
614
576
|
|
|
615
577
|
log.success(
|
|
@@ -677,8 +639,6 @@ export async function generateTemplates(options: GenerateOptions): Promise<Gener
|
|
|
677
639
|
const derivedValue = context.selfConfig[variable.name];
|
|
678
640
|
if (derivedValue === undefined) continue;
|
|
679
641
|
|
|
680
|
-
const stringValue =
|
|
681
|
-
typeof derivedValue === 'string' ? derivedValue : JSON.stringify(derivedValue);
|
|
682
642
|
const existing = db
|
|
683
643
|
.select()
|
|
684
644
|
.from(moduleConfigs)
|
|
@@ -686,14 +646,16 @@ export async function generateTemplates(options: GenerateOptions): Promise<Gener
|
|
|
686
646
|
.get();
|
|
687
647
|
|
|
688
648
|
if (!existing) {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
.
|
|
649
|
+
// Insert-only (not upsert) — derived values only seed on first
|
|
650
|
+
// generate; subsequent runs preserve whatever the user has set.
|
|
651
|
+
// Routes through upsertModuleConfig anyway because the helper
|
|
652
|
+
// is the only path that maintains the valueJson invariant.
|
|
653
|
+
upsertModuleConfig(
|
|
654
|
+
db,
|
|
655
|
+
moduleId,
|
|
656
|
+
variable.name,
|
|
657
|
+
derivedValue as string | number | boolean | unknown[] | Record<string, unknown>,
|
|
658
|
+
);
|
|
697
659
|
}
|
|
698
660
|
}
|
|
699
661
|
}
|
|
@@ -787,33 +749,12 @@ export async function generateTemplates(options: GenerateOptions): Promise<Gener
|
|
|
787
749
|
|
|
788
750
|
if (value !== undefined) {
|
|
789
751
|
// Store in module_configs so it appears in host_vars
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
.
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
const isString = typeof value === 'string';
|
|
798
|
-
if (existing) {
|
|
799
|
-
db.update(moduleConfigs)
|
|
800
|
-
.set({
|
|
801
|
-
value: isString ? stringValue : '',
|
|
802
|
-
valueJson: isString ? null : stringValue,
|
|
803
|
-
updatedAt: new Date(),
|
|
804
|
-
})
|
|
805
|
-
.where(eq(moduleConfigs.id, existing.id))
|
|
806
|
-
.run();
|
|
807
|
-
} else {
|
|
808
|
-
db.insert(moduleConfigs)
|
|
809
|
-
.values({
|
|
810
|
-
moduleId,
|
|
811
|
-
key: imp.name,
|
|
812
|
-
value: isString ? stringValue : '',
|
|
813
|
-
valueJson: isString ? null : stringValue,
|
|
814
|
-
})
|
|
815
|
-
.run();
|
|
816
|
-
}
|
|
752
|
+
upsertModuleConfig(
|
|
753
|
+
db,
|
|
754
|
+
moduleId,
|
|
755
|
+
imp.name,
|
|
756
|
+
value as string | number | boolean | unknown[] | Record<string, unknown>,
|
|
757
|
+
);
|
|
817
758
|
|
|
818
759
|
// Redact sensitive values in logs
|
|
819
760
|
const isSensitive =
|
|
@@ -1000,21 +941,10 @@ export async function generateTemplates(options: GenerateOptions): Promise<Gener
|
|
|
1000
941
|
),
|
|
1001
942
|
)
|
|
1002
943
|
.get();
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
.run();
|
|
1008
|
-
} else {
|
|
1009
|
-
db.insert(moduleConfigs)
|
|
1010
|
-
.values({
|
|
1011
|
-
moduleId,
|
|
1012
|
-
key: 'registrar_tasks_path',
|
|
1013
|
-
value: tasksPathValue,
|
|
1014
|
-
valueJson: null,
|
|
1015
|
-
})
|
|
1016
|
-
.run();
|
|
1017
|
-
}
|
|
944
|
+
upsertModuleConfig(db, moduleId, 'registrar_tasks_path', tasksPathValue);
|
|
945
|
+
// (existingTasksPath select above is now redundant — left for
|
|
946
|
+
// the broader refactor; upsertModuleConfig is idempotent.)
|
|
947
|
+
void existingTasksPath;
|
|
1018
948
|
}
|
|
1019
949
|
}
|
|
1020
950
|
}
|
|
@@ -1024,24 +954,7 @@ export async function generateTemplates(options: GenerateOptions): Promise<Gener
|
|
|
1024
954
|
// Modules with build artifacts (e.g., compiled binaries, static assets) need
|
|
1025
955
|
// this path so Ansible can copy them to the target machine.
|
|
1026
956
|
if (manifest.build?.artifacts && manifest.build.artifacts.length > 0) {
|
|
1027
|
-
|
|
1028
|
-
.select()
|
|
1029
|
-
.from(moduleConfigs)
|
|
1030
|
-
.where(
|
|
1031
|
-
and(eq(moduleConfigs.moduleId, moduleId), eq(moduleConfigs.key, 'build_artifacts_dir')),
|
|
1032
|
-
)
|
|
1033
|
-
.get();
|
|
1034
|
-
|
|
1035
|
-
if (!existing) {
|
|
1036
|
-
db.insert(moduleConfigs)
|
|
1037
|
-
.values({ moduleId, key: 'build_artifacts_dir', value: modulePath })
|
|
1038
|
-
.run();
|
|
1039
|
-
} else {
|
|
1040
|
-
db.update(moduleConfigs)
|
|
1041
|
-
.set({ value: modulePath, updatedAt: new Date() })
|
|
1042
|
-
.where(eq(moduleConfigs.id, existing.id))
|
|
1043
|
-
.run();
|
|
1044
|
-
}
|
|
957
|
+
upsertModuleConfig(db, moduleId, 'build_artifacts_dir', modulePath);
|
|
1045
958
|
}
|
|
1046
959
|
|
|
1047
960
|
// Execution: Generate Ansible inventory structure
|
|
@@ -67,8 +67,11 @@ export async function setupIntegrationTest(): Promise<IntegrationTestContext> {
|
|
|
67
67
|
// the same path so subprocess + responder share the bus DB.
|
|
68
68
|
const busDbPath = join(dataDir, 'events.db');
|
|
69
69
|
|
|
70
|
-
// CLI with isolated environment
|
|
71
|
-
|
|
70
|
+
// CLI with isolated environment. CELILO_SUPPRESS_DEPRECATION silences
|
|
71
|
+
// the legacy-path banners (e.g. on `celilo system init`) so they
|
|
72
|
+
// don't pollute test output — the banners are an operator-UX concern,
|
|
73
|
+
// not a CI signal.
|
|
74
|
+
const cli = `CELILO_DB_PATH="${dbPath}" CELILO_DATA_DIR="${dataDir}" CELILO_SUPPRESS_DEPRECATION=1 bun run src/cli/index.ts`;
|
|
72
75
|
|
|
73
76
|
// Note: Migrations are auto-run by createDbClient() in setupTestDatabaseFile()
|
|
74
77
|
|
|
@@ -87,6 +87,14 @@ export interface Machine {
|
|
|
87
87
|
assignedModuleIds: string[];
|
|
88
88
|
/** Module ID this machine is earmarked for, or null/undefined */
|
|
89
89
|
earmarkedModule?: string | null;
|
|
90
|
+
/**
|
|
91
|
+
* Appliance / API-only machines (e.g., greenwave) where celilo has
|
|
92
|
+
* no shell access. Base-module aspects skip these — see
|
|
93
|
+
* v2/CELILO_BASE.md D8. Optional on the type because most call
|
|
94
|
+
* sites don't care; the DB column defaults to false so the
|
|
95
|
+
* effective value is well-defined regardless.
|
|
96
|
+
*/
|
|
97
|
+
apiOnly?: boolean;
|
|
90
98
|
createdAt: Date;
|
|
91
99
|
updatedAt: Date;
|
|
92
100
|
}
|