@celilo/cli 0.1.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 +1566 -0
- package/bin/celilo +16 -0
- package/drizzle/0000_complex_puma.sql +179 -0
- package/drizzle/0001_dizzy_wolfpack.sql +2 -0
- package/drizzle/0002_web_routes.sql +16 -0
- package/drizzle/0003_backup_storage.sql +32 -0
- package/drizzle/meta/0000_snapshot.json +1151 -0
- package/drizzle/meta/0001_snapshot.json +1167 -0
- package/drizzle/meta/0002_snapshot.json +1257 -0
- package/drizzle/meta/_journal.json +27 -0
- package/package.json +64 -0
- package/schemas/system_config.json +106 -0
- package/src/__integration__/container-services-cli.integration.test.ts +246 -0
- package/src/ansible/dependencies.test.ts +309 -0
- package/src/ansible/dependencies.ts +896 -0
- package/src/ansible/inventory.test.ts +463 -0
- package/src/ansible/inventory.ts +445 -0
- package/src/ansible/secrets.ts +222 -0
- package/src/ansible/validation.test.ts +92 -0
- package/src/ansible/validation.ts +272 -0
- package/src/api-clients/digitalocean.ts +94 -0
- package/src/api-clients/proxmox.ts +655 -0
- package/src/capabilities/logging-wrapper.test.ts +217 -0
- package/src/capabilities/lookup.test.ts +149 -0
- package/src/capabilities/lookup.ts +89 -0
- package/src/capabilities/public-web-helpers.test.ts +198 -0
- package/src/capabilities/public-web-publish.test.ts +458 -0
- package/src/capabilities/registration.test.ts +395 -0
- package/src/capabilities/registration.ts +200 -0
- package/src/capabilities/route-validation.test.ts +121 -0
- package/src/capabilities/route-validation.ts +96 -0
- package/src/capabilities/secret-ref.test.ts +313 -0
- package/src/capabilities/secret-validation.ts +157 -0
- package/src/capabilities/secrets.test.ts +750 -0
- package/src/capabilities/secrets.ts +244 -0
- package/src/capabilities/validation.test.ts +613 -0
- package/src/capabilities/validation.ts +160 -0
- package/src/capabilities/well-known.test.ts +238 -0
- package/src/capabilities/well-known.ts +222 -0
- package/src/cli/cli.test.ts +654 -0
- package/src/cli/command-registry.ts +742 -0
- package/src/cli/command-tree-parser.test.ts +180 -0
- package/src/cli/command-tree-parser.ts +193 -0
- package/src/cli/commands/backup-create.ts +137 -0
- package/src/cli/commands/backup-delete.ts +74 -0
- package/src/cli/commands/backup-import.ts +97 -0
- package/src/cli/commands/backup-list.ts +132 -0
- package/src/cli/commands/backup-name.ts +73 -0
- package/src/cli/commands/backup-prune.ts +98 -0
- package/src/cli/commands/backup-restore.ts +122 -0
- package/src/cli/commands/capability-info.ts +121 -0
- package/src/cli/commands/capability-list.ts +47 -0
- package/src/cli/commands/completion.ts +87 -0
- package/src/cli/commands/hook-run.ts +176 -0
- package/src/cli/commands/ipam.ts +607 -0
- package/src/cli/commands/machine-add.ts +235 -0
- package/src/cli/commands/machine-earmark.ts +82 -0
- package/src/cli/commands/machine-list.ts +77 -0
- package/src/cli/commands/machine-remove.ts +90 -0
- package/src/cli/commands/machine-status.ts +131 -0
- package/src/cli/commands/module-audit.ts +51 -0
- package/src/cli/commands/module-build.ts +60 -0
- package/src/cli/commands/module-config.ts +170 -0
- package/src/cli/commands/module-deploy.ts +71 -0
- package/src/cli/commands/module-generate.ts +236 -0
- package/src/cli/commands/module-health.ts +108 -0
- package/src/cli/commands/module-import.ts +80 -0
- package/src/cli/commands/module-list.ts +43 -0
- package/src/cli/commands/module-logs.ts +73 -0
- package/src/cli/commands/module-remove.ts +162 -0
- package/src/cli/commands/module-show.ts +208 -0
- package/src/cli/commands/module-status.ts +131 -0
- package/src/cli/commands/module-types.ts +189 -0
- package/src/cli/commands/module-upgrade.ts +192 -0
- package/src/cli/commands/package.ts +68 -0
- package/src/cli/commands/secret-list.ts +99 -0
- package/src/cli/commands/secret-set.ts +134 -0
- package/src/cli/commands/service-add-digitalocean.ts +133 -0
- package/src/cli/commands/service-add-proxmox.ts +342 -0
- package/src/cli/commands/service-config-get.ts +83 -0
- package/src/cli/commands/service-config-set.ts +145 -0
- package/src/cli/commands/service-list.ts +74 -0
- package/src/cli/commands/service-reconfigure.ts +230 -0
- package/src/cli/commands/service-remove.ts +103 -0
- package/src/cli/commands/service-verify.ts +240 -0
- package/src/cli/commands/status.ts +216 -0
- package/src/cli/commands/storage-add-local.ts +106 -0
- package/src/cli/commands/storage-add-s3.ts +114 -0
- package/src/cli/commands/storage-list.ts +72 -0
- package/src/cli/commands/storage-remove.ts +54 -0
- package/src/cli/commands/storage-set-default.ts +44 -0
- package/src/cli/commands/storage-verify.ts +54 -0
- package/src/cli/commands/system-config.ts +168 -0
- package/src/cli/commands/system-init.ts +314 -0
- package/src/cli/commands/system-secret-get.ts +98 -0
- package/src/cli/commands/system-secret-set.ts +76 -0
- package/src/cli/commands/system-vault-password.ts +34 -0
- package/src/cli/completion.test.ts +37 -0
- package/src/cli/completion.ts +482 -0
- package/src/cli/fuel-gauge.test.ts +208 -0
- package/src/cli/fuel-gauge.ts +405 -0
- package/src/cli/generate-zsh-completion.test.ts +95 -0
- package/src/cli/generate-zsh-completion.ts +497 -0
- package/src/cli/index.ts +1583 -0
- package/src/cli/interactive-config.test.ts +201 -0
- package/src/cli/interactive-config.ts +62 -0
- package/src/cli/parser.test.ts +227 -0
- package/src/cli/parser.ts +244 -0
- package/src/cli/prompts.test.ts +33 -0
- package/src/cli/prompts.ts +121 -0
- package/src/cli/types.ts +38 -0
- package/src/cli/validators.test.ts +235 -0
- package/src/cli/validators.ts +188 -0
- package/src/config/env.ts +41 -0
- package/src/config/paths.test.ts +172 -0
- package/src/config/paths.ts +108 -0
- package/src/db/client.ts +190 -0
- package/src/db/migrate.ts +30 -0
- package/src/db/schema.test.ts +221 -0
- package/src/db/schema.ts +434 -0
- package/src/hooks/capability-loader-firewall.test.ts +246 -0
- package/src/hooks/capability-loader.test.ts +100 -0
- package/src/hooks/capability-loader.ts +520 -0
- package/src/hooks/define-hook.test.ts +488 -0
- package/src/hooks/executor.test.ts +462 -0
- package/src/hooks/executor.ts +469 -0
- package/src/hooks/logger.test.ts +54 -0
- package/src/hooks/logger.ts +95 -0
- package/src/hooks/test-fixtures/failing-hook.ts +13 -0
- package/src/hooks/test-fixtures/no-default-hook.ts +6 -0
- package/src/hooks/test-fixtures/success-hook.ts +20 -0
- package/src/hooks/test-fixtures/unbranded-hook.ts +11 -0
- package/src/hooks/test-fixtures/void-hook.ts +13 -0
- package/src/hooks/types.ts +89 -0
- package/src/infrastructure/property-extractor.test.ts +194 -0
- package/src/infrastructure/property-extractor.ts +151 -0
- package/src/ipam/allocator.test.ts +442 -0
- package/src/ipam/allocator.ts +369 -0
- package/src/ipam/auto-allocator.test.ts +247 -0
- package/src/ipam/auto-allocator.ts +270 -0
- package/src/ipam/subnet-parser.test.ts +107 -0
- package/src/ipam/subnet-parser.ts +136 -0
- package/src/manifest/contracts/index.ts +61 -0
- package/src/manifest/contracts/v1.ts +118 -0
- package/src/manifest/json-schema-roundtrip.test.ts +99 -0
- package/src/manifest/schema.ts +367 -0
- package/src/manifest/template-validator.test.ts +231 -0
- package/src/manifest/template-validator.ts +322 -0
- package/src/manifest/validate.test.ts +1180 -0
- package/src/manifest/validate.ts +415 -0
- package/src/module/import.test.ts +355 -0
- package/src/module/import.ts +676 -0
- package/src/module/packaging/audit.ts +169 -0
- package/src/module/packaging/build.ts +228 -0
- package/src/module/packaging/checksum.ts +41 -0
- package/src/module/packaging/extract.ts +234 -0
- package/src/module/packaging/signature.ts +47 -0
- package/src/secrets/encryption.test.ts +284 -0
- package/src/secrets/encryption.ts +162 -0
- package/src/secrets/generators.test.ts +112 -0
- package/src/secrets/generators.ts +127 -0
- package/src/secrets/master-key.test.ts +159 -0
- package/src/secrets/master-key.ts +114 -0
- package/src/secrets/storage.test.ts +115 -0
- package/src/secrets/storage.ts +106 -0
- package/src/secrets/vault.test.ts +35 -0
- package/src/secrets/vault.ts +42 -0
- package/src/services/backup-create.ts +532 -0
- package/src/services/backup-metadata.ts +198 -0
- package/src/services/backup-restore.ts +229 -0
- package/src/services/backup-retention.ts +84 -0
- package/src/services/backup-storage.ts +281 -0
- package/src/services/build-stream.test.ts +122 -0
- package/src/services/build-stream.ts +201 -0
- package/src/services/config-interview.ts +694 -0
- package/src/services/container-service.test.ts +298 -0
- package/src/services/container-service.ts +401 -0
- package/src/services/cross-module-data-manager.test.ts +405 -0
- package/src/services/cross-module-data-manager.ts +412 -0
- package/src/services/deploy-ansible.ts +88 -0
- package/src/services/deploy-planner.ts +153 -0
- package/src/services/deploy-preflight.ts +274 -0
- package/src/services/deploy-ssh.ts +131 -0
- package/src/services/deploy-terraform.test.ts +55 -0
- package/src/services/deploy-terraform.ts +445 -0
- package/src/services/deploy-validation.ts +311 -0
- package/src/services/dns-auto-register.ts +211 -0
- package/src/services/health-runner.ts +184 -0
- package/src/services/infrastructure-selector.test.ts +485 -0
- package/src/services/infrastructure-selector.ts +245 -0
- package/src/services/infrastructure-variable-resolver.test.ts +751 -0
- package/src/services/infrastructure-variable-resolver.ts +234 -0
- package/src/services/machine-detector.ts +328 -0
- package/src/services/machine-pool.test.ts +405 -0
- package/src/services/machine-pool.ts +316 -0
- package/src/services/manifest-validation.ts +120 -0
- package/src/services/module-build.test.ts +290 -0
- package/src/services/module-build.ts +431 -0
- package/src/services/module-config.test.ts +237 -0
- package/src/services/module-config.ts +298 -0
- package/src/services/module-deploy.ts +862 -0
- package/src/services/module-types-drift.test.ts +73 -0
- package/src/services/module-types-generator.test.ts +288 -0
- package/src/services/module-types-generator.ts +189 -0
- package/src/services/proxmox-state-recovery.ts +140 -0
- package/src/services/schema-validation.ts +155 -0
- package/src/services/secret-schema-loader.test.ts +311 -0
- package/src/services/secret-schema-loader.ts +239 -0
- package/src/services/ssh-key-manager.test.ts +283 -0
- package/src/services/ssh-key-manager.ts +193 -0
- package/src/services/storage-providers/local.ts +105 -0
- package/src/services/storage-providers/s3.ts +182 -0
- package/src/services/storage-providers/types.ts +24 -0
- package/src/services/system-config-schema-types.ts +25 -0
- package/src/services/system-config-validator.test.ts +160 -0
- package/src/services/system-config-validator.ts +74 -0
- package/src/services/system-init.test.ts +153 -0
- package/src/services/system-init.ts +253 -0
- package/src/services/terraform-safety.ts +174 -0
- package/src/services/zone-detector.test.ts +110 -0
- package/src/services/zone-detector.ts +102 -0
- package/src/services/zone-policy.test.ts +97 -0
- package/src/services/zone-policy.ts +126 -0
- package/src/templates/generator.test.ts +645 -0
- package/src/templates/generator.ts +1119 -0
- package/src/templates/types.ts +62 -0
- package/src/test-utils/INTERACTIVE_PROMPTS.md +167 -0
- package/src/test-utils/cli-context-interactive.test.ts +152 -0
- package/src/test-utils/cli-context-server.test.ts +66 -0
- package/src/test-utils/cli-context.test.ts +273 -0
- package/src/test-utils/cli-context.ts +677 -0
- package/src/test-utils/cli-result.test.ts +282 -0
- package/src/test-utils/cli-result.ts +241 -0
- package/src/test-utils/cli.ts +55 -0
- package/src/test-utils/completion-harness.test.ts +126 -0
- package/src/test-utils/completion-harness.ts +82 -0
- package/src/test-utils/database.test.ts +182 -0
- package/src/test-utils/database.ts +126 -0
- package/src/test-utils/filesystem.test.ts +208 -0
- package/src/test-utils/filesystem.ts +142 -0
- package/src/test-utils/fixtures.test.ts +123 -0
- package/src/test-utils/fixtures.ts +160 -0
- package/src/test-utils/golden-diff.ts +197 -0
- package/src/test-utils/index.ts +77 -0
- package/src/test-utils/integration.ts +81 -0
- package/src/test-utils/module-fixtures.ts +468 -0
- package/src/test-utils/modules.test.ts +144 -0
- package/src/test-utils/modules.ts +183 -0
- package/src/test-utils/setup-test-db.ts +90 -0
- package/src/test-utils/value-extractor.test.ts +231 -0
- package/src/test-utils/value-extractor.ts +228 -0
- package/src/types/infrastructure.ts +157 -0
- package/src/utils/shell.test.ts +365 -0
- package/src/utils/shell.ts +159 -0
- package/src/validation/schemas.ts +166 -0
- package/src/variables/ansible-resolver.test.ts +142 -0
- package/src/variables/ansible-resolver.ts +69 -0
- package/src/variables/capability-self-ref.test.ts +220 -0
- package/src/variables/context.test.ts +1265 -0
- package/src/variables/context.ts +624 -0
- package/src/variables/declarative-derivation.test.ts +743 -0
- package/src/variables/declarative-derivation.ts +200 -0
- package/src/variables/parser.test.ts +231 -0
- package/src/variables/parser.ts +76 -0
- package/src/variables/resolver.test.ts +458 -0
- package/src/variables/resolver.ts +282 -0
- package/src/variables/types.ts +59 -0
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { unlink } from 'node:fs/promises';
|
|
4
|
+
import { type DbClient, createDbClient } from '../db/client';
|
|
5
|
+
import {
|
|
6
|
+
ipAllocations,
|
|
7
|
+
ipReservations,
|
|
8
|
+
modules,
|
|
9
|
+
systemConfig,
|
|
10
|
+
vmidReservations,
|
|
11
|
+
} from '../db/schema';
|
|
12
|
+
import type { NewModule } from '../db/schema';
|
|
13
|
+
import {
|
|
14
|
+
allocateIPFromSubnet,
|
|
15
|
+
allocateResources,
|
|
16
|
+
allocateVMID,
|
|
17
|
+
deallocateResources,
|
|
18
|
+
getAllocatedIPsInSubnet,
|
|
19
|
+
getAllocation,
|
|
20
|
+
isIPAvailable,
|
|
21
|
+
isVMIDAvailable,
|
|
22
|
+
listReservations,
|
|
23
|
+
listVMIDReservations,
|
|
24
|
+
reserveIP,
|
|
25
|
+
reserveVMID,
|
|
26
|
+
unreserveIP,
|
|
27
|
+
unreserveVMID,
|
|
28
|
+
} from './allocator';
|
|
29
|
+
|
|
30
|
+
describe('IPAM Allocator', () => {
|
|
31
|
+
let db: DbClient;
|
|
32
|
+
let testDbPath: string;
|
|
33
|
+
|
|
34
|
+
beforeEach(async () => {
|
|
35
|
+
testDbPath = `./test-ipam-${Date.now()}-${Math.random()}.db`;
|
|
36
|
+
db = createDbClient({ path: testDbPath });
|
|
37
|
+
|
|
38
|
+
// Tables are automatically created by auto-migration in createDbClient()
|
|
39
|
+
|
|
40
|
+
// Insert test module
|
|
41
|
+
const testModule: NewModule = {
|
|
42
|
+
id: 'test-module',
|
|
43
|
+
name: 'Test Module',
|
|
44
|
+
version: '1.0.0',
|
|
45
|
+
sourcePath: '/test',
|
|
46
|
+
manifestData: {},
|
|
47
|
+
};
|
|
48
|
+
db.insert(modules).values(testModule).run();
|
|
49
|
+
|
|
50
|
+
// Insert network config for DMZ zone
|
|
51
|
+
db.insert(systemConfig)
|
|
52
|
+
.values({
|
|
53
|
+
key: 'network.dmz.subnet',
|
|
54
|
+
value: '10.0.10.0/24',
|
|
55
|
+
})
|
|
56
|
+
.run();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
afterEach(async () => {
|
|
60
|
+
db.$client.close();
|
|
61
|
+
if (existsSync(testDbPath)) {
|
|
62
|
+
await unlink(testDbPath);
|
|
63
|
+
}
|
|
64
|
+
const walPath = `${testDbPath}-wal`;
|
|
65
|
+
const shmPath = `${testDbPath}-shm`;
|
|
66
|
+
if (existsSync(walPath)) {
|
|
67
|
+
await unlink(walPath);
|
|
68
|
+
}
|
|
69
|
+
if (existsSync(shmPath)) {
|
|
70
|
+
await unlink(shmPath);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('allocateVMID', () => {
|
|
75
|
+
test('should allocate first VMID as 2100', async () => {
|
|
76
|
+
const vmid = await allocateVMID(db);
|
|
77
|
+
expect(vmid).toBe(2100);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('should increment VMID sequentially', async () => {
|
|
81
|
+
// Manually insert allocation
|
|
82
|
+
db.insert(ipAllocations)
|
|
83
|
+
.values({
|
|
84
|
+
moduleId: 'test-module',
|
|
85
|
+
vmid: 2100,
|
|
86
|
+
containerIp: '10.0.10.10/24',
|
|
87
|
+
zone: 'dmz',
|
|
88
|
+
})
|
|
89
|
+
.run();
|
|
90
|
+
|
|
91
|
+
const vmid = await allocateVMID(db);
|
|
92
|
+
expect(vmid).toBe(2101);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('should find max VMID from multiple allocations', async () => {
|
|
96
|
+
db.insert(ipAllocations)
|
|
97
|
+
.values([
|
|
98
|
+
{ moduleId: 'test-module', vmid: 2100, containerIp: '10.0.10.10/24', zone: 'dmz' },
|
|
99
|
+
{ moduleId: 'test-module', vmid: 2105, containerIp: '10.0.10.11/24', zone: 'dmz' },
|
|
100
|
+
{ moduleId: 'test-module', vmid: 2102, containerIp: '10.0.10.12/24', zone: 'dmz' },
|
|
101
|
+
])
|
|
102
|
+
.run();
|
|
103
|
+
|
|
104
|
+
const vmid = await allocateVMID(db);
|
|
105
|
+
expect(vmid).toBe(2106);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('allocateIPFromSubnet', () => {
|
|
110
|
+
test('should allocate first IP as .10', async () => {
|
|
111
|
+
const ip = await allocateIPFromSubnet('10.0.10.0/24', 'dmz', db);
|
|
112
|
+
expect(ip).toBe('10.0.10.10/24');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('should skip already allocated IPs', async () => {
|
|
116
|
+
// Allocate .10
|
|
117
|
+
db.insert(ipAllocations)
|
|
118
|
+
.values({
|
|
119
|
+
moduleId: 'test-module',
|
|
120
|
+
vmid: 2100,
|
|
121
|
+
containerIp: '10.0.10.10/24',
|
|
122
|
+
zone: 'dmz',
|
|
123
|
+
})
|
|
124
|
+
.run();
|
|
125
|
+
|
|
126
|
+
// Next allocation should be .11
|
|
127
|
+
const ip = await allocateIPFromSubnet('10.0.10.0/24', 'dmz', db);
|
|
128
|
+
expect(ip).toBe('10.0.10.11/24');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('should skip reserved IPs', async () => {
|
|
132
|
+
// Reserve .10 and .11
|
|
133
|
+
db.insert(ipReservations)
|
|
134
|
+
.values({
|
|
135
|
+
ipStart: '10.0.10.10',
|
|
136
|
+
ipEnd: '10.0.10.11',
|
|
137
|
+
zone: 'dmz',
|
|
138
|
+
reason: 'Test reservation',
|
|
139
|
+
})
|
|
140
|
+
.run();
|
|
141
|
+
|
|
142
|
+
// Should allocate .12
|
|
143
|
+
const ip = await allocateIPFromSubnet('10.0.10.0/24', 'dmz', db);
|
|
144
|
+
expect(ip).toBe('10.0.10.12/24');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('should skip single IP reservation', async () => {
|
|
148
|
+
// Reserve .10 only
|
|
149
|
+
db.insert(ipReservations)
|
|
150
|
+
.values({
|
|
151
|
+
ipStart: '10.0.10.10',
|
|
152
|
+
ipEnd: null,
|
|
153
|
+
zone: 'dmz',
|
|
154
|
+
reason: 'Single IP reservation',
|
|
155
|
+
})
|
|
156
|
+
.run();
|
|
157
|
+
|
|
158
|
+
// Should allocate .11
|
|
159
|
+
const ip = await allocateIPFromSubnet('10.0.10.0/24', 'dmz', db);
|
|
160
|
+
expect(ip).toBe('10.0.10.11/24');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('should throw error when subnet exhausted', async () => {
|
|
164
|
+
// Fill up subnet (.10 to .254 = 245 IPs)
|
|
165
|
+
const allocations = [];
|
|
166
|
+
for (let i = 10; i <= 254; i++) {
|
|
167
|
+
allocations.push({
|
|
168
|
+
moduleId: 'test-module',
|
|
169
|
+
vmid: 2000 + i,
|
|
170
|
+
containerIp: `10.0.10.${i}/24`,
|
|
171
|
+
zone: 'dmz' as const,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
db.insert(ipAllocations).values(allocations).run();
|
|
175
|
+
|
|
176
|
+
await expect(allocateIPFromSubnet('10.0.10.0/24', 'dmz', db)).rejects.toThrow(
|
|
177
|
+
'No available IPs in subnet',
|
|
178
|
+
);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('allocateResources', () => {
|
|
183
|
+
test('should allocate both VMID and IP', async () => {
|
|
184
|
+
const result = await allocateResources('test-module', 'dmz', db);
|
|
185
|
+
|
|
186
|
+
expect(result.vmid).toBe(2100);
|
|
187
|
+
expect(result.containerIp).toBe('10.0.10.10/24');
|
|
188
|
+
|
|
189
|
+
// Verify stored in database
|
|
190
|
+
const allocations = await db.select().from(ipAllocations).all();
|
|
191
|
+
expect(allocations).toHaveLength(1);
|
|
192
|
+
expect(allocations[0].moduleId).toBe('test-module');
|
|
193
|
+
expect(allocations[0].vmid).toBe(2100);
|
|
194
|
+
expect(allocations[0].containerIp).toBe('10.0.10.10/24');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('should throw error if zone not configured', async () => {
|
|
198
|
+
await expect(allocateResources('test-module', 'app', db)).rejects.toThrow(
|
|
199
|
+
"Network zone 'app' not configured",
|
|
200
|
+
);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test('should allocate different resources for multiple modules', async () => {
|
|
204
|
+
// Insert additional test modules
|
|
205
|
+
db.insert(modules)
|
|
206
|
+
.values([
|
|
207
|
+
{
|
|
208
|
+
id: 'module1',
|
|
209
|
+
name: 'Module 1',
|
|
210
|
+
version: '1.0.0',
|
|
211
|
+
sourcePath: '/test1',
|
|
212
|
+
manifestData: {},
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
id: 'module2',
|
|
216
|
+
name: 'Module 2',
|
|
217
|
+
version: '1.0.0',
|
|
218
|
+
sourcePath: '/test2',
|
|
219
|
+
manifestData: {},
|
|
220
|
+
},
|
|
221
|
+
])
|
|
222
|
+
.run();
|
|
223
|
+
|
|
224
|
+
const result1 = await allocateResources('module1', 'dmz', db);
|
|
225
|
+
const result2 = await allocateResources('module2', 'dmz', db);
|
|
226
|
+
|
|
227
|
+
expect(result1.vmid).toBe(2100);
|
|
228
|
+
expect(result1.containerIp).toBe('10.0.10.10/24');
|
|
229
|
+
|
|
230
|
+
expect(result2.vmid).toBe(2101);
|
|
231
|
+
expect(result2.containerIp).toBe('10.0.10.11/24');
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
describe('deallocateResources', () => {
|
|
236
|
+
test('should remove allocation', async () => {
|
|
237
|
+
await allocateResources('test-module', 'dmz', db);
|
|
238
|
+
|
|
239
|
+
await deallocateResources('test-module', db);
|
|
240
|
+
|
|
241
|
+
const allocations = await db.select().from(ipAllocations).all();
|
|
242
|
+
expect(allocations).toHaveLength(0);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
describe('getAllocation', () => {
|
|
247
|
+
test('should return allocation for module', async () => {
|
|
248
|
+
await allocateResources('test-module', 'dmz', db);
|
|
249
|
+
|
|
250
|
+
const allocation = await getAllocation('test-module', db);
|
|
251
|
+
|
|
252
|
+
expect(allocation).toBeDefined();
|
|
253
|
+
expect(allocation?.vmid).toBe(2100);
|
|
254
|
+
expect(allocation?.containerIp).toBe('10.0.10.10/24');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test('should return null if no allocation', async () => {
|
|
258
|
+
const allocation = await getAllocation('nonexistent', db);
|
|
259
|
+
expect(allocation).toBeNull();
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe('IP Reservations', () => {
|
|
264
|
+
test('should reserve single IP', async () => {
|
|
265
|
+
await reserveIP('10.0.10.50', 'dmz', 'Load balancer', null, db);
|
|
266
|
+
|
|
267
|
+
const reservations = await db.select().from(ipReservations).all();
|
|
268
|
+
expect(reservations).toHaveLength(1);
|
|
269
|
+
expect(reservations[0].ipStart).toBe('10.0.10.50');
|
|
270
|
+
expect(reservations[0].ipEnd).toBeNull();
|
|
271
|
+
expect(reservations[0].reason).toBe('Load balancer');
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test('should reserve IP range', async () => {
|
|
275
|
+
await reserveIP('10.0.10.100', 'dmz', 'Manual VMs', '10.0.10.110', db);
|
|
276
|
+
|
|
277
|
+
const reservations = await db.select().from(ipReservations).all();
|
|
278
|
+
expect(reservations).toHaveLength(1);
|
|
279
|
+
expect(reservations[0].ipStart).toBe('10.0.10.100');
|
|
280
|
+
expect(reservations[0].ipEnd).toBe('10.0.10.110');
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test('should unreserve IP', async () => {
|
|
284
|
+
await reserveIP('10.0.10.50', 'dmz', 'Test', null, db);
|
|
285
|
+
await unreserveIP('10.0.10.50', 'dmz', db);
|
|
286
|
+
|
|
287
|
+
const reservations = await db.select().from(ipReservations).all();
|
|
288
|
+
expect(reservations).toHaveLength(0);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test('should list all reservations', async () => {
|
|
292
|
+
await reserveIP('10.0.10.50', 'dmz', 'LB', null, db);
|
|
293
|
+
await reserveIP('10.0.10.100', 'dmz', 'VMs', '10.0.10.110', db);
|
|
294
|
+
|
|
295
|
+
const reservations = await listReservations(db);
|
|
296
|
+
expect(reservations).toHaveLength(2);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe('getAllocatedIPsInSubnet', () => {
|
|
301
|
+
test('should return allocated IPs in subnet', async () => {
|
|
302
|
+
// Insert additional test modules
|
|
303
|
+
db.insert(modules)
|
|
304
|
+
.values([
|
|
305
|
+
{
|
|
306
|
+
id: 'module1',
|
|
307
|
+
name: 'Module 1',
|
|
308
|
+
version: '1.0.0',
|
|
309
|
+
sourcePath: '/test1',
|
|
310
|
+
manifestData: {},
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
id: 'module2',
|
|
314
|
+
name: 'Module 2',
|
|
315
|
+
version: '1.0.0',
|
|
316
|
+
sourcePath: '/test2',
|
|
317
|
+
manifestData: {},
|
|
318
|
+
},
|
|
319
|
+
])
|
|
320
|
+
.run();
|
|
321
|
+
|
|
322
|
+
await allocateResources('module1', 'dmz', db);
|
|
323
|
+
await allocateResources('module2', 'dmz', db);
|
|
324
|
+
|
|
325
|
+
const ips = await getAllocatedIPsInSubnet('10.0.10.0/24', db);
|
|
326
|
+
|
|
327
|
+
expect(ips).toHaveLength(2);
|
|
328
|
+
expect(ips).toContain('10.0.10.10/24');
|
|
329
|
+
expect(ips).toContain('10.0.10.11/24');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test('should return empty array if no allocations', async () => {
|
|
333
|
+
const ips = await getAllocatedIPsInSubnet('10.0.10.0/24', db);
|
|
334
|
+
expect(ips).toHaveLength(0);
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
describe('isIPAvailable', () => {
|
|
339
|
+
test('should return true for available IP', async () => {
|
|
340
|
+
const available = await isIPAvailable('10.0.10.50/24', 'dmz', db);
|
|
341
|
+
expect(available).toBe(true);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test('should return false for allocated IP', async () => {
|
|
345
|
+
await allocateResources('test-module', 'dmz', db);
|
|
346
|
+
|
|
347
|
+
const available = await isIPAvailable('10.0.10.10/24', 'dmz', db);
|
|
348
|
+
expect(available).toBe(false);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test('should return false for reserved IP', async () => {
|
|
352
|
+
await reserveIP('10.0.10.50', 'dmz', 'Test', null, db);
|
|
353
|
+
|
|
354
|
+
const available = await isIPAvailable('10.0.10.50/24', 'dmz', db);
|
|
355
|
+
expect(available).toBe(false);
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
describe('VMID Reservations', () => {
|
|
360
|
+
test('should reserve VMID', async () => {
|
|
361
|
+
await reserveVMID(2100, 'Test reservation', db);
|
|
362
|
+
|
|
363
|
+
const reservations = await db.select().from(vmidReservations).all();
|
|
364
|
+
expect(reservations).toHaveLength(1);
|
|
365
|
+
expect(reservations[0].vmid).toBe(2100);
|
|
366
|
+
expect(reservations[0].reason).toBe('Test reservation');
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test('should throw error if VMID already allocated', async () => {
|
|
370
|
+
await allocateResources('test-module', 'dmz', db);
|
|
371
|
+
|
|
372
|
+
await expect(reserveVMID(2100, 'Test', db)).rejects.toThrow(
|
|
373
|
+
'VMID 2100 is already allocated to module test-module',
|
|
374
|
+
);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test('should throw error if VMID already reserved', async () => {
|
|
378
|
+
await reserveVMID(2100, 'First reservation', db);
|
|
379
|
+
|
|
380
|
+
await expect(reserveVMID(2100, 'Second reservation', db)).rejects.toThrow(
|
|
381
|
+
'VMID 2100 is already reserved: First reservation',
|
|
382
|
+
);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test('should unreserve VMID', async () => {
|
|
386
|
+
await reserveVMID(2100, 'Test', db);
|
|
387
|
+
await unreserveVMID(2100, db);
|
|
388
|
+
|
|
389
|
+
const reservations = await db.select().from(vmidReservations).all();
|
|
390
|
+
expect(reservations).toHaveLength(0);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
test('should list VMID reservations', async () => {
|
|
394
|
+
await reserveVMID(2100, 'First', db);
|
|
395
|
+
await reserveVMID(2101, 'Second', db);
|
|
396
|
+
|
|
397
|
+
const reservations = await listVMIDReservations(db);
|
|
398
|
+
expect(reservations).toHaveLength(2);
|
|
399
|
+
expect(reservations[0].vmid).toBe(2100);
|
|
400
|
+
expect(reservations[1].vmid).toBe(2101);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test('should skip reserved VMIDs during allocation', async () => {
|
|
404
|
+
await reserveVMID(2100, 'Reserved for external VM', db);
|
|
405
|
+
|
|
406
|
+
const vmid = await allocateVMID(db);
|
|
407
|
+
|
|
408
|
+
expect(vmid).toBe(2101);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
test('should skip multiple reserved VMIDs', async () => {
|
|
412
|
+
await reserveVMID(2100, 'First', db);
|
|
413
|
+
await reserveVMID(2101, 'Second', db);
|
|
414
|
+
await reserveVMID(2102, 'Third', db);
|
|
415
|
+
|
|
416
|
+
const vmid = await allocateVMID(db);
|
|
417
|
+
|
|
418
|
+
expect(vmid).toBe(2103);
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
describe('isVMIDAvailable', () => {
|
|
423
|
+
test('should return true for available VMID', async () => {
|
|
424
|
+
const available = await isVMIDAvailable(2100, db);
|
|
425
|
+
expect(available).toBe(true);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
test('should return false for allocated VMID', async () => {
|
|
429
|
+
await allocateResources('test-module', 'dmz', db);
|
|
430
|
+
|
|
431
|
+
const available = await isVMIDAvailable(2100, db);
|
|
432
|
+
expect(available).toBe(false);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
test('should return false for reserved VMID', async () => {
|
|
436
|
+
await reserveVMID(2100, 'Test', db);
|
|
437
|
+
|
|
438
|
+
const available = await isVMIDAvailable(2100, db);
|
|
439
|
+
expect(available).toBe(false);
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
});
|