@celilo/cli 0.13.2 → 0.13.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celilo/cli",
3
- "version": "0.13.2",
3
+ "version": "0.13.3",
4
4
  "description": "Celilo — home lab orchestration CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -6,7 +6,7 @@ import type { DbClient } from '../db/client';
6
6
  import { machines, moduleConfigs, systemConfig } from '../db/schema';
7
7
  import { getModuleSystems } from '../services/deployed-systems';
8
8
  import { parseStoredConfigValue } from '../services/module-config';
9
- import { getTempKeyPath } from '../services/ssh-key-manager';
9
+ import { LOCAL_MACHINE_IP, getTempKeyPath } from '../services/ssh-key-manager';
10
10
 
11
11
  /**
12
12
  * Inventory generation result
@@ -395,7 +395,7 @@ export async function generateInventory(
395
395
 
396
396
  // Build host definition from machine. The local box (127.0.0.1)
397
397
  // uses Ansible's local connection — no SSH key/host.
398
- const isLocal = machine.ipAddress === '127.0.0.1';
398
+ const isLocal = machine.ipAddress === LOCAL_MACHINE_IP;
399
399
  host = {
400
400
  hostname: moduleHostname || machine.hostname,
401
401
  ansibleHost: machine.ipAddress,
@@ -51,7 +51,7 @@ import { executeAnsible } from './deploy-ansible';
51
51
  import { getContainerSystemsInZones } from './deployed-systems';
52
52
  import { getSystemsByZone } from './machine-pool';
53
53
  import { executeProxmoxReconcile, planProxmoxReconcile } from './proxmox-reconcile';
54
- import { writeTemporarySshKey } from './ssh-key-manager';
54
+ import { LOCAL_MACHINE_IP, writeTemporarySshKey } from './ssh-key-manager';
55
55
 
56
56
  type DbClient = ReturnType<typeof getDb>;
57
57
 
@@ -231,7 +231,7 @@ export async function materializeAspectAnsible(args: {
231
231
  // (ansible/inventory.ts). Without this the fan-out tried `ssh root@127.0.0.1`
232
232
  // with an empty key file and the host failed UNREACHABLE with an opaque
233
233
  // "error in libcrypto", so celilo could not configure its own resolv.conf.
234
- const isLocal = t.ipAddress === '127.0.0.1';
234
+ const isLocal = t.ipAddress === LOCAL_MACHINE_IP;
235
235
  const keyPath = t.machineId && !isLocal ? await writeTemporarySshKey(t.machineId) : undefined;
236
236
  inventoryHosts.push({
237
237
  hostname: t.hostname,
@@ -40,7 +40,7 @@ import { resolveInfrastructureVariables } from './infrastructure-variable-resolv
40
40
  import { findMachineForModule } from './machine-pool';
41
41
  import { checkProxmoxReachable, formatProxmoxUnreachableError } from './proxmox-preflight';
42
42
  import { republishStaticWebConsumers } from './public-web-republish';
43
- import { deleteTemporarySshKey, writeTemporarySshKey } from './ssh-key-manager';
43
+ import { LOCAL_MACHINE_IP, deleteTemporarySshKey, writeTemporarySshKey } from './ssh-key-manager';
44
44
  import { buildTerraformEnvForService } from './terraform-env';
45
45
 
46
46
  export interface DeployResult {
@@ -1129,8 +1129,20 @@ async function deployModuleImpl(
1129
1129
 
1130
1130
  let machineId: string | undefined;
1131
1131
  if (plan.infrastructure?.type === 'machine' && plan.infrastructure.machineId) {
1132
- machineId = plan.infrastructure.machineId;
1133
- await writeTemporarySshKey(machineId);
1132
+ // The management box deploys to ITSELF (registered in the machine pool as
1133
+ // 127.0.0.1) over Ansible's local connection — it has no stored SSH key,
1134
+ // and pinning one here throws before Ansible ever runs. `inventory.ts` and
1135
+ // `aspect-runner.ts` both already skip the key for the local box; this call
1136
+ // site did not, so celilo-mgmt could not complete a self-deploy.
1137
+ const machine = db
1138
+ .select()
1139
+ .from(machines)
1140
+ .where(eq(machines.id, plan.infrastructure.machineId))
1141
+ .get();
1142
+ if (machine?.ipAddress !== LOCAL_MACHINE_IP) {
1143
+ machineId = plan.infrastructure.machineId;
1144
+ await writeTemporarySshKey(machineId);
1145
+ }
1134
1146
  }
1135
1147
 
1136
1148
  try {
@@ -10,6 +10,7 @@ import { closeDb } from '../db/client';
10
10
  import { runMigrations } from '../db/migrate';
11
11
  import { addMachine } from './machine-pool';
12
12
  import {
13
+ LOCAL_MACHINE_IP,
13
14
  ManagedSshKey,
14
15
  cleanupTemporarySshKeys,
15
16
  deleteTemporarySshKey,
@@ -326,3 +327,16 @@ describe('ssh-key-manager', () => {
326
327
  });
327
328
  });
328
329
  });
330
+
331
+ // The management box is reached over Ansible's local connection, never by
332
+ // `ssh root@127.0.0.1`, so callers must skip key materialization for it. Three
333
+ // call sites decide this — inventory.ts, aspect-runner.ts and module-deploy.ts —
334
+ // and module-deploy.ts did NOT, so `celilo module deploy celilo-mgmt` threw
335
+ // "has no SSH key stored" before Ansible ever ran and the control plane could
336
+ // not self-deploy. The literal now lives in one place so a fourth caller finds
337
+ // it instead of retyping it.
338
+ describe('LOCAL_MACHINE_IP', () => {
339
+ it('is the loopback address the management box registers itself under', () => {
340
+ expect(LOCAL_MACHINE_IP).toBe('127.0.0.1');
341
+ });
342
+ });
@@ -17,6 +17,18 @@ function getTempKeysDir(): string {
17
17
  return join(tmpdir(), 'celilo-ansible-keys');
18
18
  }
19
19
 
20
+ /**
21
+ * The address the management box registers itself under in its own machine pool.
22
+ *
23
+ * celilo-mgr deploys to ITSELF, so it appears in the pool as a machine — but it
24
+ * has no stored SSH key and must be reached over Ansible's local connection
25
+ * rather than `ssh root@127.0.0.1`. Three call sites need to know this
26
+ * (`inventory.ts`, `aspect-runner.ts`, `module-deploy.ts`); one of them silently
27
+ * didn't, and celilo-mgmt could not complete a self-deploy as a result. Exported
28
+ * so the next caller finds it instead of retyping the literal.
29
+ */
30
+ export const LOCAL_MACHINE_IP = '127.0.0.1';
31
+
20
32
  /**
21
33
  * Ensure the temp keys directory exists
22
34
  */