@pleri/olam-cli 0.1.151 → 0.1.153
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/dist/commands/doctor.d.ts +53 -2
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +74 -11
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/host-cp.d.ts.map +1 -1
- package/dist/commands/host-cp.js +17 -0
- package/dist/commands/host-cp.js.map +1 -1
- package/dist/commands/services.d.ts.map +1 -1
- package/dist/commands/services.js +9 -26
- package/dist/commands/services.js.map +1 -1
- package/dist/commands/substrate.d.ts +27 -0
- package/dist/commands/substrate.d.ts.map +1 -1
- package/dist/commands/substrate.js +27 -8
- package/dist/commands/substrate.js.map +1 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +11 -0
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/image-digests.json +7 -7
- package/dist/index.js +1747 -1293
- package/dist/lib/auth-refresh-kubernetes.d.ts +3 -0
- package/dist/lib/auth-refresh-kubernetes.d.ts.map +1 -1
- package/dist/lib/auth-refresh-kubernetes.js +6 -17
- package/dist/lib/auth-refresh-kubernetes.js.map +1 -1
- package/dist/lib/health-probes.d.ts +43 -0
- package/dist/lib/health-probes.d.ts.map +1 -1
- package/dist/lib/health-probes.js +106 -0
- package/dist/lib/health-probes.js.map +1 -1
- package/dist/lib/k8s-bootstrap.d.ts +120 -0
- package/dist/lib/k8s-bootstrap.d.ts.map +1 -0
- package/dist/lib/k8s-bootstrap.js +193 -0
- package/dist/lib/k8s-bootstrap.js.map +1 -0
- package/dist/lib/k8s-secret-render.d.ts +139 -0
- package/dist/lib/k8s-secret-render.d.ts.map +1 -0
- package/dist/lib/k8s-secret-render.js +281 -0
- package/dist/lib/k8s-secret-render.js.map +1 -0
- package/dist/lib/kubectl-context.d.ts +38 -0
- package/dist/lib/kubectl-context.d.ts.map +1 -0
- package/dist/lib/kubectl-context.js +43 -0
- package/dist/lib/kubectl-context.js.map +1 -0
- package/dist/lib/upgrade-kubernetes.d.ts +24 -1
- package/dist/lib/upgrade-kubernetes.d.ts.map +1 -1
- package/dist/lib/upgrade-kubernetes.js +90 -38
- package/dist/lib/upgrade-kubernetes.js.map +1 -1
- package/host-cp/k8s/manifests/50-deployment.yaml +8 -1
- package/host-cp/k8s/manifests/auth-service/50-deployment.yaml +4 -1
- package/host-cp/k8s/manifests/kg-service/50-deployment.yaml +4 -1
- package/host-cp/k8s/manifests/mcp-auth-service/50-deployment.yaml +4 -1
- package/host-cp/k8s/manifests/memory-service/50-deployment.yaml +4 -1
- package/host-cp/src/docker-events.mjs +19 -4
- package/host-cp/src/server.mjs +18 -1
- package/package.json +1 -1
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* k8s-bootstrap.ts — apply the `olam` namespace + RBAC + secret set on a
|
|
3
|
+
* fresh kubernetes cluster.
|
|
4
|
+
*
|
|
5
|
+
* B4 of olam-issue-npm-only-k3s. Without this an npm-only operator running
|
|
6
|
+
* `olam upgrade --substrate=kubernetes` against a fresh cluster fails with:
|
|
7
|
+
*
|
|
8
|
+
* Error from server (NotFound): namespaces "olam" not found
|
|
9
|
+
* Secret "olam-host-cp-secret" not found in namespace "olam"
|
|
10
|
+
*
|
|
11
|
+
* The 8-step upgrade flow (upgrade-kubernetes.ts) already runs
|
|
12
|
+
* `seedManifestsFromBundle` to put manifest YAML at ~/.olam/k8s/manifests/,
|
|
13
|
+
* but no one APPLIES them on first install — operators were expected to
|
|
14
|
+
* `kubectl apply -f` by hand. That barrier is the npm-only blocker this
|
|
15
|
+
* module closes.
|
|
16
|
+
*
|
|
17
|
+
* Idempotency: kubectl apply is idempotent by design for manifests
|
|
18
|
+
* (server-side merge keys deduplicate). For secrets we render once and
|
|
19
|
+
* persist the values to ~/.olam/k8s-secrets-state.json — subsequent runs
|
|
20
|
+
* reuse the same values so worlds that have cached tokens don't break.
|
|
21
|
+
*
|
|
22
|
+
* Order (matches the seed manifest filename numeric prefix):
|
|
23
|
+
* 1. 00-namespace.yaml — host-cp namespace
|
|
24
|
+
* 2. 10-serviceaccount.yaml — host-cp ServiceAccount
|
|
25
|
+
* 3. 20-rbac.yaml — host-cp Role/RoleBinding
|
|
26
|
+
* 4. 30-configmap.yaml — host-cp env ConfigMap
|
|
27
|
+
* 5. 45-pvc.yaml — host-cp PersistentVolumeClaim
|
|
28
|
+
* 6. host-cp Secret — RENDERED from templates/40-secret-template.yaml
|
|
29
|
+
* 7. per-peripheral manifests + secrets (auth-service, mcp-auth-service, kg-service, memory-service)
|
|
30
|
+
*
|
|
31
|
+
* Deployments + Services (50-deployment.yaml, 60-service.yaml) are NOT
|
|
32
|
+
* applied here — that is upgrade-kubernetes.ts's job (it manages rollout
|
|
33
|
+
* status + port-forward + audit log). ensureK8sBootstrap returns once the
|
|
34
|
+
* "prerequisites" (namespace + RBAC + secrets) are in place.
|
|
35
|
+
*/
|
|
36
|
+
import { spawnSync } from 'node:child_process';
|
|
37
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
38
|
+
import { join } from 'node:path';
|
|
39
|
+
import { kubectlWrap } from './kubectl-wrap.js';
|
|
40
|
+
import { installRoot } from '../install-root.js';
|
|
41
|
+
import { OLAM_HOME } from './config.js';
|
|
42
|
+
import { renderAllSecrets, SECRET_TEMPLATE_BINDINGS, } from './k8s-secret-render.js';
|
|
43
|
+
export const K8S_NAMESPACE = 'olam';
|
|
44
|
+
/**
|
|
45
|
+
* Manifest files inside `<install-root>/host-cp/k8s/manifests/` that
|
|
46
|
+
* ensureK8sBootstrap applies. Ordered numerically by filename prefix.
|
|
47
|
+
* Deployment + Service are intentionally absent — upgrade-kubernetes.ts
|
|
48
|
+
* applies those alongside its rollout-status step.
|
|
49
|
+
*/
|
|
50
|
+
const HOST_CP_PREREQ_MANIFESTS = [
|
|
51
|
+
'00-namespace.yaml',
|
|
52
|
+
'10-serviceaccount.yaml',
|
|
53
|
+
'20-rbac.yaml',
|
|
54
|
+
'30-configmap.yaml',
|
|
55
|
+
'45-pvc.yaml',
|
|
56
|
+
];
|
|
57
|
+
/**
|
|
58
|
+
* Peripheral subdirs under `<install-root>/host-cp/k8s/manifests/`. Each
|
|
59
|
+
* carries its own 10/20/30/45 prerequisite YAMLs. 50-deployment + 60-service
|
|
60
|
+
* are skipped here (upgrade.ts owns rollout).
|
|
61
|
+
*/
|
|
62
|
+
const PERIPHERAL_SUBDIRS = ['auth-service', 'mcp-auth-service', 'kg-service', 'memory-service'];
|
|
63
|
+
const PERIPHERAL_PREREQ_FILES = ['10-serviceaccount.yaml', '20-rbac.yaml', '30-configmap.yaml', '45-pvc.yaml'];
|
|
64
|
+
/**
|
|
65
|
+
* kubectl apply wrapper — accepts a YAML string and applies via stdin (D20
|
|
66
|
+
* stdin-safe; values never inlined into argv). When `manifestPath` is given
|
|
67
|
+
* the file is applied via `-f <path>` instead (manifests stay on disk).
|
|
68
|
+
*/
|
|
69
|
+
async function kubectlApply(context, source, deps) {
|
|
70
|
+
const wrap = deps.kubectlWrapImpl ?? kubectlWrap;
|
|
71
|
+
if ('path' in source) {
|
|
72
|
+
const r = await wrap(['--context', context, 'apply', '-f', source.path], { timeout: 30_000 });
|
|
73
|
+
return { ok: r.ok, stderr: r.stderr };
|
|
74
|
+
}
|
|
75
|
+
const r = await wrap(['--context', context, 'apply', '-f', '-'], { timeout: 30_000, stdin: source.stdinYaml });
|
|
76
|
+
return { ok: r.ok, stderr: r.stderr };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Locate the bundled k8s assets directory. In install-mode this is
|
|
80
|
+
* `<install-root>/host-cp/k8s/`. In dev-mode it falls back to the
|
|
81
|
+
* monorepo's `packages/host-cp/k8s/`.
|
|
82
|
+
*/
|
|
83
|
+
export function resolveK8sAssetsRoot(installRootDir = installRoot()) {
|
|
84
|
+
const installed = join(installRootDir, 'host-cp', 'k8s');
|
|
85
|
+
if (existsSync(installed))
|
|
86
|
+
return installed;
|
|
87
|
+
// Dev-mode fallback. installRoot points at <repo>/packages/cli; walk up.
|
|
88
|
+
const repoRoot = join(installRootDir, '..', '..');
|
|
89
|
+
const monorepo = join(repoRoot, 'packages', 'host-cp', 'k8s');
|
|
90
|
+
if (existsSync(monorepo))
|
|
91
|
+
return monorepo;
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Run the bootstrap. Idempotent on re-run.
|
|
96
|
+
*
|
|
97
|
+
* Apply order (numeric filename prefix mirrors apply ORDER):
|
|
98
|
+
* 1. <root>/manifests/00-namespace.yaml + 10/20/30/45 (host-cp prereqs)
|
|
99
|
+
* 2. Per-peripheral 10/20/30/45 prereqs (auth-service / mcp-auth-service / kg-service / memory-service)
|
|
100
|
+
* 3. host-cp Secret (rendered from templates/40-secret-template.yaml)
|
|
101
|
+
* 4. Per-peripheral Secrets (rendered from each *-secret-template.yaml)
|
|
102
|
+
*
|
|
103
|
+
* On the first failed apply we surface the kubectl stderr and stop —
|
|
104
|
+
* partial-apply state is left in the cluster (kubectl apply is idempotent;
|
|
105
|
+
* re-running picks up where we stopped).
|
|
106
|
+
*/
|
|
107
|
+
export async function ensureK8sBootstrap(opts, deps = {}) {
|
|
108
|
+
const stdout = deps.stdout ?? process.stdout;
|
|
109
|
+
const stderr = deps.stderr ?? process.stderr;
|
|
110
|
+
const namespace = opts.namespace ?? K8S_NAMESPACE;
|
|
111
|
+
const apply = deps.applyImpl ?? kubectlApply;
|
|
112
|
+
const assetsRoot = deps.k8sAssetsRoot ?? resolveK8sAssetsRoot();
|
|
113
|
+
if (assetsRoot === null || !existsSync(assetsRoot)) {
|
|
114
|
+
return {
|
|
115
|
+
exitCode: 1,
|
|
116
|
+
result: { applied: [], skipped: [], secretResults: [] },
|
|
117
|
+
error: 'Could not find bundled k8s assets. Expected <install-root>/host-cp/k8s/ or <repo>/packages/host-cp/k8s/. ' +
|
|
118
|
+
'Reinstall the CLI: `npm install -g @pleri/olam-cli@latest`.',
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const manifestsRoot = join(assetsRoot, 'manifests');
|
|
122
|
+
const applied = [];
|
|
123
|
+
const skipped = [];
|
|
124
|
+
// 1+2. host-cp prereq manifests + per-peripheral prereqs.
|
|
125
|
+
const manifestPaths = [];
|
|
126
|
+
for (const f of HOST_CP_PREREQ_MANIFESTS) {
|
|
127
|
+
manifestPaths.push(join(manifestsRoot, f));
|
|
128
|
+
}
|
|
129
|
+
for (const sub of PERIPHERAL_SUBDIRS) {
|
|
130
|
+
for (const f of PERIPHERAL_PREREQ_FILES) {
|
|
131
|
+
const p = join(manifestsRoot, sub, f);
|
|
132
|
+
if (existsSync(p))
|
|
133
|
+
manifestPaths.push(p);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
for (const mp of manifestPaths) {
|
|
137
|
+
if (opts.dryRun) {
|
|
138
|
+
applied.push({ kind: 'manifest', path: mp });
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (!existsSync(mp)) {
|
|
142
|
+
skipped.push({ kind: 'manifest', ref: mp, reason: 'file not found in bundle (older package?)' });
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
stdout.write(` → applying ${mp.replace(assetsRoot, '<k8s-assets>')}\n`);
|
|
146
|
+
const r = await apply(opts.context, { path: mp }, deps);
|
|
147
|
+
if (!r.ok) {
|
|
148
|
+
stderr.write(`error: kubectl apply -f ${mp} failed:\n${r.stderr}\n`);
|
|
149
|
+
return {
|
|
150
|
+
exitCode: 1,
|
|
151
|
+
result: { applied, skipped, secretResults: [] },
|
|
152
|
+
error: `kubectl apply of ${mp} failed`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
applied.push({ kind: 'manifest', path: mp });
|
|
156
|
+
}
|
|
157
|
+
// 3+4. Render + apply secrets.
|
|
158
|
+
const { results } = renderAllSecrets(assetsRoot, { rotate: opts.rotateSecrets === true, context: opts.context, namespace }, deps.renderDeps ?? {});
|
|
159
|
+
for (const r of results) {
|
|
160
|
+
if (r.status === 'skipped') {
|
|
161
|
+
const reason = `unresolved source(s): ${r.missingSources.join('; ')}`;
|
|
162
|
+
skipped.push({ kind: 'secret', ref: r.secretName, reason });
|
|
163
|
+
stderr.write(`warn: Secret ${r.secretName} skipped — ${reason}\n`);
|
|
164
|
+
stderr.write(` Once you have the source value, apply manually:\n`);
|
|
165
|
+
stderr.write(` kubectl --context ${opts.context} apply -n ${namespace} -f - <<EOF\n (rendered secret YAML)\nEOF\n`);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (opts.dryRun) {
|
|
169
|
+
applied.push({ kind: 'secret', name: r.secretName });
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
stdout.write(` → applying Secret ${r.secretName}\n`);
|
|
173
|
+
const a = await apply(opts.context, { stdinYaml: r.renderedYaml }, deps);
|
|
174
|
+
if (!a.ok) {
|
|
175
|
+
stderr.write(`error: kubectl apply Secret ${r.secretName} failed:\n${a.stderr}\n`);
|
|
176
|
+
return {
|
|
177
|
+
exitCode: 1,
|
|
178
|
+
result: { applied, skipped, secretResults: results },
|
|
179
|
+
error: `kubectl apply Secret ${r.secretName} failed`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
applied.push({ kind: 'secret', name: r.secretName });
|
|
183
|
+
}
|
|
184
|
+
return { exitCode: 0, result: { applied, skipped, secretResults: results } };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Read the list of expected Secret names — used by probeK8sRequiredSecrets
|
|
188
|
+
* so the doctor probe stays in lockstep with the bootstrap module.
|
|
189
|
+
*/
|
|
190
|
+
export function expectedSecretNames() {
|
|
191
|
+
return SECRET_TEMPLATE_BINDINGS.map((b) => b.secretName);
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=k8s-bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"k8s-bootstrap.js","sourceRoot":"","sources":["../../src/lib/k8s-bootstrap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* k8s-secret-render.ts — render `REPLACE_ME_FROM_*` placeholders in the
|
|
3
|
+
* shipped k8s Secret templates with real values, and persist the rendered
|
|
4
|
+
* set so reapply is idempotent.
|
|
5
|
+
*
|
|
6
|
+
* B5 of olam-issue-npm-only-k3s (npm-only operator bootstrap completion).
|
|
7
|
+
*
|
|
8
|
+
* The templates at `packages/host-cp/k8s/templates/*-secret-template.yaml`
|
|
9
|
+
* carry placeholders like `REPLACE_ME_FROM_HOME_DOTOLAM_AUTH_SECRET`. The
|
|
10
|
+
* source-of-truth value for each placeholder is documented in the template
|
|
11
|
+
* comment block; it's either a host-side file in `~/.olam/` (the compose
|
|
12
|
+
* bootstrap path) or a subprocess output (e.g. `gh auth token`).
|
|
13
|
+
*
|
|
14
|
+
* Source resolution per placeholder:
|
|
15
|
+
*
|
|
16
|
+
* placeholder → source
|
|
17
|
+
* REPLACE_ME_FROM_HOME_DOTOLAM_AUTH_SECRET → file ~/.olam/auth-secret (generate if missing)
|
|
18
|
+
* REPLACE_ME_FROM_GH_AUTH_TOKEN → subprocess `gh auth token` (warn + skip if absent)
|
|
19
|
+
* REPLACE_ME_FROM_HOME_DOTOLAM_AUTH_DB_SECRET → file ~/.olam/auth-db-secret (generate if missing)
|
|
20
|
+
* REPLACE_ME_FROM_HOME_DOTOLAM_MCP_AUTH_JWT_SECRET → file ~/.olam/mcp-auth-jwt-secret (generate if missing)
|
|
21
|
+
* REPLACE_ME_FROM_HOME_DOTOLAM_KG_BEARER_TOKEN → file ~/.olam/kg-bearer-token (generate if missing)
|
|
22
|
+
* REPLACE_ME_FROM_HOME_DOTOLAM_MEMORY_BEARER_SECRET → file ~/.olam/memory-bearer-secret (generate if missing)
|
|
23
|
+
*
|
|
24
|
+
* State persistence: `~/.olam/k8s-secrets-state.json` records the rendered
|
|
25
|
+
* Secret set so re-runs (idempotent reapply) reuse the same values. Without
|
|
26
|
+
* the state file every `olam upgrade` would rotate tokens silently, breaking
|
|
27
|
+
* worlds that have cached the previous value. Operator opts in to rotation
|
|
28
|
+
* via `--rotate-secrets`.
|
|
29
|
+
*
|
|
30
|
+
* D20 stdin-safe design: rendered YAML is built in-process and applied via
|
|
31
|
+
* stdin to `kubectl apply -f -`. Placeholder values are NEVER inlined into
|
|
32
|
+
* subprocess argv (audit:auth-callers + general security hygiene).
|
|
33
|
+
*/
|
|
34
|
+
export declare const K8S_SECRETS_STATE_PATH: string;
|
|
35
|
+
/**
|
|
36
|
+
* One template's placeholder set + the per-host-file source it reads from.
|
|
37
|
+
* Hardcoded mapping — every secret template's `# Source:` comment names the
|
|
38
|
+
* file; the audit:cli-bundle-k8s gate ensures the templates stay in sync.
|
|
39
|
+
*/
|
|
40
|
+
export interface SecretTemplateBinding {
|
|
41
|
+
readonly secretName: string;
|
|
42
|
+
readonly templateRelPath: string;
|
|
43
|
+
readonly placeholders: ReadonlyArray<{
|
|
44
|
+
readonly placeholder: string;
|
|
45
|
+
readonly key: string;
|
|
46
|
+
readonly source: {
|
|
47
|
+
readonly kind: 'file';
|
|
48
|
+
readonly hostFile: string;
|
|
49
|
+
} | {
|
|
50
|
+
readonly kind: 'gh-token';
|
|
51
|
+
};
|
|
52
|
+
}>;
|
|
53
|
+
}
|
|
54
|
+
/** Canonical mapping for the 5 secret templates that ship with the CLI. */
|
|
55
|
+
export declare const SECRET_TEMPLATE_BINDINGS: ReadonlyArray<SecretTemplateBinding>;
|
|
56
|
+
/** Per-secret state record — keys map to RENDERED values reused on re-apply. */
|
|
57
|
+
export interface RenderedSecret {
|
|
58
|
+
readonly keys: Record<string, string>;
|
|
59
|
+
readonly skipped?: ReadonlyArray<string>;
|
|
60
|
+
}
|
|
61
|
+
/** State file shape. version=1 lets us migrate the schema without breaking older state. */
|
|
62
|
+
export interface K8sSecretsState {
|
|
63
|
+
readonly version: 1;
|
|
64
|
+
readonly context: string;
|
|
65
|
+
readonly namespace: string;
|
|
66
|
+
readonly generatedAt: string;
|
|
67
|
+
readonly secrets: Record<string, RenderedSecret>;
|
|
68
|
+
}
|
|
69
|
+
/** Injectable deps so tests don't touch the real filesystem / shell. */
|
|
70
|
+
export interface RenderDeps {
|
|
71
|
+
readonly olamHome?: string;
|
|
72
|
+
readonly statePath?: string;
|
|
73
|
+
readonly readFile?: (path: string, enc: 'utf8') => string;
|
|
74
|
+
readonly writeFile?: (path: string, data: string, mode: number) => void;
|
|
75
|
+
readonly fileExists?: (path: string) => boolean;
|
|
76
|
+
readonly genRandomHex?: () => string;
|
|
77
|
+
readonly runGhTokenCmd?: () => {
|
|
78
|
+
ok: boolean;
|
|
79
|
+
token: string;
|
|
80
|
+
};
|
|
81
|
+
readonly readState?: () => K8sSecretsState | null;
|
|
82
|
+
readonly writeState?: (state: K8sSecretsState) => void;
|
|
83
|
+
readonly stderr?: NodeJS.WritableStream;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Read the state file. Returns null when missing OR when the version doesn't match (forward-compat).
|
|
87
|
+
*/
|
|
88
|
+
export declare function readSecretsState(statePath?: string): K8sSecretsState | null;
|
|
89
|
+
/** Atomically write the state file (0600). */
|
|
90
|
+
export declare function writeSecretsState(state: K8sSecretsState, statePath?: string): void;
|
|
91
|
+
/**
|
|
92
|
+
* Result of rendering one Secret. Either:
|
|
93
|
+
* - resolved: every placeholder got a value (rendered YAML is ready to apply).
|
|
94
|
+
* - skipped: at least one source was unavailable AND not generatable (e.g.
|
|
95
|
+
* gh auth token absent); the operator must apply this secret
|
|
96
|
+
* manually OR install gh + retry.
|
|
97
|
+
*/
|
|
98
|
+
export interface RenderResult {
|
|
99
|
+
readonly secretName: string;
|
|
100
|
+
readonly status: 'resolved' | 'skipped';
|
|
101
|
+
readonly renderedYaml?: string;
|
|
102
|
+
readonly keys: Record<string, string>;
|
|
103
|
+
readonly missingSources: ReadonlyArray<string>;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Substitute the placeholder strings in the template YAML with real values.
|
|
107
|
+
* Builds a fresh Secret YAML string (does NOT touch the template on disk).
|
|
108
|
+
*
|
|
109
|
+
* The templates hold ONE placeholder per stringData key; literal string
|
|
110
|
+
* substitution suffices. We avoid YAML parsing to keep the template comment
|
|
111
|
+
* block (which carries operator-facing documentation) intact in the rendered
|
|
112
|
+
* output.
|
|
113
|
+
*/
|
|
114
|
+
export declare function substitutePlaceholders(templateYaml: string, values: Record<string, string>): string;
|
|
115
|
+
/**
|
|
116
|
+
* Render ONE secret template. Reads the template YAML from `templatesRoot`
|
|
117
|
+
* (resolved by the caller to either source or install-mode location),
|
|
118
|
+
* resolves each placeholder, persists generated values to host files,
|
|
119
|
+
* substitutes, and returns the rendered YAML.
|
|
120
|
+
*
|
|
121
|
+
* `reuse` carries the prior state's per-key values when an unrotated
|
|
122
|
+
* re-apply is requested; null means "this is the first apply or operator
|
|
123
|
+
* asked for rotation."
|
|
124
|
+
*/
|
|
125
|
+
export declare function renderOneSecret(binding: SecretTemplateBinding, templatesRoot: string, reuse: RenderedSecret | null, deps?: RenderDeps, rotate?: boolean): RenderResult;
|
|
126
|
+
/**
|
|
127
|
+
* Render the full set of shipped Secret templates. `rotate=true` discards
|
|
128
|
+
* the on-disk state and regenerates every value; `rotate=false` reuses
|
|
129
|
+
* state for unchanged secrets so worlds caching old tokens don't break.
|
|
130
|
+
*/
|
|
131
|
+
export declare function renderAllSecrets(templatesRoot: string, opts: {
|
|
132
|
+
rotate: boolean;
|
|
133
|
+
context: string;
|
|
134
|
+
namespace: string;
|
|
135
|
+
}, deps?: RenderDeps): {
|
|
136
|
+
results: ReadonlyArray<RenderResult>;
|
|
137
|
+
nextState: K8sSecretsState;
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=k8s-secret-render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"k8s-secret-render.d.ts","sourceRoot":"","sources":["../../src/lib/k8s-secret-render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* k8s-secret-render.ts — render `REPLACE_ME_FROM_*` placeholders in the
|
|
3
|
+
* shipped k8s Secret templates with real values, and persist the rendered
|
|
4
|
+
* set so reapply is idempotent.
|
|
5
|
+
*
|
|
6
|
+
* B5 of olam-issue-npm-only-k3s (npm-only operator bootstrap completion).
|
|
7
|
+
*
|
|
8
|
+
* The templates at `packages/host-cp/k8s/templates/*-secret-template.yaml`
|
|
9
|
+
* carry placeholders like `REPLACE_ME_FROM_HOME_DOTOLAM_AUTH_SECRET`. The
|
|
10
|
+
* source-of-truth value for each placeholder is documented in the template
|
|
11
|
+
* comment block; it's either a host-side file in `~/.olam/` (the compose
|
|
12
|
+
* bootstrap path) or a subprocess output (e.g. `gh auth token`).
|
|
13
|
+
*
|
|
14
|
+
* Source resolution per placeholder:
|
|
15
|
+
*
|
|
16
|
+
* placeholder → source
|
|
17
|
+
* REPLACE_ME_FROM_HOME_DOTOLAM_AUTH_SECRET → file ~/.olam/auth-secret (generate if missing)
|
|
18
|
+
* REPLACE_ME_FROM_GH_AUTH_TOKEN → subprocess `gh auth token` (warn + skip if absent)
|
|
19
|
+
* REPLACE_ME_FROM_HOME_DOTOLAM_AUTH_DB_SECRET → file ~/.olam/auth-db-secret (generate if missing)
|
|
20
|
+
* REPLACE_ME_FROM_HOME_DOTOLAM_MCP_AUTH_JWT_SECRET → file ~/.olam/mcp-auth-jwt-secret (generate if missing)
|
|
21
|
+
* REPLACE_ME_FROM_HOME_DOTOLAM_KG_BEARER_TOKEN → file ~/.olam/kg-bearer-token (generate if missing)
|
|
22
|
+
* REPLACE_ME_FROM_HOME_DOTOLAM_MEMORY_BEARER_SECRET → file ~/.olam/memory-bearer-secret (generate if missing)
|
|
23
|
+
*
|
|
24
|
+
* State persistence: `~/.olam/k8s-secrets-state.json` records the rendered
|
|
25
|
+
* Secret set so re-runs (idempotent reapply) reuse the same values. Without
|
|
26
|
+
* the state file every `olam upgrade` would rotate tokens silently, breaking
|
|
27
|
+
* worlds that have cached the previous value. Operator opts in to rotation
|
|
28
|
+
* via `--rotate-secrets`.
|
|
29
|
+
*
|
|
30
|
+
* D20 stdin-safe design: rendered YAML is built in-process and applied via
|
|
31
|
+
* stdin to `kubectl apply -f -`. Placeholder values are NEVER inlined into
|
|
32
|
+
* subprocess argv (audit:auth-callers + general security hygiene).
|
|
33
|
+
*/
|
|
34
|
+
import { spawnSync } from 'node:child_process';
|
|
35
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync, statSync } from 'node:fs';
|
|
36
|
+
import { join, dirname } from 'node:path';
|
|
37
|
+
import { randomBytes } from 'node:crypto';
|
|
38
|
+
import { OLAM_HOME } from './config.js';
|
|
39
|
+
export const K8S_SECRETS_STATE_PATH = join(OLAM_HOME, 'k8s-secrets-state.json');
|
|
40
|
+
const SECRET_HEX_BYTES = 32; // 64-char hex tokens — mirrors memory-secret.ts SECRET_LEN_BYTES.
|
|
41
|
+
/** Canonical mapping for the 5 secret templates that ship with the CLI. */
|
|
42
|
+
export const SECRET_TEMPLATE_BINDINGS = [
|
|
43
|
+
{
|
|
44
|
+
secretName: 'olam-host-cp-secret',
|
|
45
|
+
templateRelPath: 'templates/40-secret-template.yaml',
|
|
46
|
+
placeholders: [
|
|
47
|
+
{
|
|
48
|
+
placeholder: 'REPLACE_ME_FROM_HOME_DOTOLAM_AUTH_SECRET',
|
|
49
|
+
key: 'OLAM_AUTH_SECRET',
|
|
50
|
+
source: { kind: 'file', hostFile: 'auth-secret' },
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
placeholder: 'REPLACE_ME_FROM_GH_AUTH_TOKEN',
|
|
54
|
+
key: 'GH_TOKEN',
|
|
55
|
+
source: { kind: 'gh-token' },
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
secretName: 'olam-auth-service-secret',
|
|
61
|
+
templateRelPath: 'templates/auth-service-secret-template.yaml',
|
|
62
|
+
placeholders: [
|
|
63
|
+
{
|
|
64
|
+
placeholder: 'REPLACE_ME_FROM_HOME_DOTOLAM_AUTH_DB_SECRET',
|
|
65
|
+
key: 'OLAM_AUTH_DB_SECRET',
|
|
66
|
+
source: { kind: 'file', hostFile: 'auth-db-secret' },
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
secretName: 'olam-mcp-auth-service-secret',
|
|
72
|
+
templateRelPath: 'templates/mcp-auth-service-secret-template.yaml',
|
|
73
|
+
placeholders: [
|
|
74
|
+
{
|
|
75
|
+
placeholder: 'REPLACE_ME_FROM_HOME_DOTOLAM_MCP_AUTH_JWT_SECRET',
|
|
76
|
+
key: 'OLAM_MCP_AUTH_JWT_SECRET',
|
|
77
|
+
source: { kind: 'file', hostFile: 'mcp-auth-jwt-secret' },
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
secretName: 'olam-kg-service-secret',
|
|
83
|
+
templateRelPath: 'templates/kg-service-secret-template.yaml',
|
|
84
|
+
placeholders: [
|
|
85
|
+
{
|
|
86
|
+
placeholder: 'REPLACE_ME_FROM_HOME_DOTOLAM_KG_BEARER_TOKEN',
|
|
87
|
+
key: 'OLAM_KG_BEARER_TOKEN',
|
|
88
|
+
source: { kind: 'file', hostFile: 'kg-bearer-token' },
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
secretName: 'olam-memory-service-secret',
|
|
94
|
+
templateRelPath: 'templates/memory-service-secret-template.yaml',
|
|
95
|
+
placeholders: [
|
|
96
|
+
{
|
|
97
|
+
placeholder: 'REPLACE_ME_FROM_HOME_DOTOLAM_MEMORY_BEARER_SECRET',
|
|
98
|
+
key: 'OLAM_MEMORY_BEARER_SECRET',
|
|
99
|
+
source: { kind: 'file', hostFile: 'memory-bearer-secret' },
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
const defaultGenRandomHex = () => randomBytes(SECRET_HEX_BYTES).toString('hex');
|
|
105
|
+
const defaultRunGhTokenCmd = () => {
|
|
106
|
+
const r = spawnSync('gh', ['auth', 'token'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
107
|
+
if (r.status === 0 && r.stdout.trim().length > 0) {
|
|
108
|
+
return { ok: true, token: r.stdout.trim() };
|
|
109
|
+
}
|
|
110
|
+
return { ok: false, token: '' };
|
|
111
|
+
};
|
|
112
|
+
const defaultReadFile = (p, enc) => readFileSync(p, enc);
|
|
113
|
+
const defaultWriteFile = (p, data, mode) => {
|
|
114
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
115
|
+
writeFileSync(p, data, { encoding: 'utf8', mode });
|
|
116
|
+
// writeFileSync's mode is umask-respecting; chmod explicitly to be safe.
|
|
117
|
+
chmodSync(p, mode);
|
|
118
|
+
};
|
|
119
|
+
const defaultFileExists = (p) => existsSync(p);
|
|
120
|
+
/**
|
|
121
|
+
* Read the state file. Returns null when missing OR when the version doesn't match (forward-compat).
|
|
122
|
+
*/
|
|
123
|
+
export function readSecretsState(statePath = K8S_SECRETS_STATE_PATH) {
|
|
124
|
+
if (!existsSync(statePath))
|
|
125
|
+
return null;
|
|
126
|
+
try {
|
|
127
|
+
const raw = readFileSync(statePath, 'utf8');
|
|
128
|
+
const parsed = JSON.parse(raw);
|
|
129
|
+
if (parsed.version !== 1)
|
|
130
|
+
return null;
|
|
131
|
+
return parsed;
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** Atomically write the state file (0600). */
|
|
138
|
+
export function writeSecretsState(state, statePath = K8S_SECRETS_STATE_PATH) {
|
|
139
|
+
mkdirSync(dirname(statePath), { recursive: true });
|
|
140
|
+
const tmp = `${statePath}.tmp.${process.pid}`;
|
|
141
|
+
writeFileSync(tmp, JSON.stringify(state, null, 2) + '\n', { encoding: 'utf8', mode: 0o600 });
|
|
142
|
+
chmodSync(tmp, 0o600);
|
|
143
|
+
// renameSync is atomic on POSIX
|
|
144
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
145
|
+
require('node:fs').renameSync(tmp, statePath);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Resolve a single placeholder's value. Returns null when the source is
|
|
149
|
+
* absent AND not generatable (gh-token only). For file-sourced placeholders
|
|
150
|
+
* we always generate if missing — that's the npm-only operator path.
|
|
151
|
+
*/
|
|
152
|
+
function resolveSourceValue(source, olamHome, reuseFromState, rotate, deps) {
|
|
153
|
+
if (!rotate && reuseFromState !== undefined && reuseFromState !== '') {
|
|
154
|
+
return { ok: true, value: reuseFromState };
|
|
155
|
+
}
|
|
156
|
+
if (source.kind === 'file') {
|
|
157
|
+
const filePath = join(olamHome, source.hostFile);
|
|
158
|
+
// Rotation: always generate fresh value, overwrite the host file.
|
|
159
|
+
if (rotate) {
|
|
160
|
+
const fresh = deps.genRandomHex();
|
|
161
|
+
deps.writeFile(filePath, fresh, 0o600);
|
|
162
|
+
return { ok: true, value: fresh };
|
|
163
|
+
}
|
|
164
|
+
if (deps.fileExists(filePath)) {
|
|
165
|
+
const value = deps.readFile(filePath, 'utf8').trim();
|
|
166
|
+
if (value.length > 0)
|
|
167
|
+
return { ok: true, value };
|
|
168
|
+
}
|
|
169
|
+
// Missing or empty — generate, persist, return.
|
|
170
|
+
const fresh = deps.genRandomHex();
|
|
171
|
+
deps.writeFile(filePath, fresh, 0o600);
|
|
172
|
+
return { ok: true, value: fresh };
|
|
173
|
+
}
|
|
174
|
+
// kind === 'gh-token' — not generatable; warn + skip if absent.
|
|
175
|
+
// Rotation reads gh CLI again (gh manages its own session rotation).
|
|
176
|
+
const r = deps.runGhTokenCmd();
|
|
177
|
+
if (r.ok)
|
|
178
|
+
return { ok: true, value: r.token };
|
|
179
|
+
return { ok: false, reason: 'gh CLI not authenticated; run `gh auth login` then re-run' };
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Substitute the placeholder strings in the template YAML with real values.
|
|
183
|
+
* Builds a fresh Secret YAML string (does NOT touch the template on disk).
|
|
184
|
+
*
|
|
185
|
+
* The templates hold ONE placeholder per stringData key; literal string
|
|
186
|
+
* substitution suffices. We avoid YAML parsing to keep the template comment
|
|
187
|
+
* block (which carries operator-facing documentation) intact in the rendered
|
|
188
|
+
* output.
|
|
189
|
+
*/
|
|
190
|
+
export function substitutePlaceholders(templateYaml, values) {
|
|
191
|
+
let out = templateYaml;
|
|
192
|
+
for (const [placeholder, value] of Object.entries(values)) {
|
|
193
|
+
// Use split/join rather than .replace to avoid regex escaping pitfalls.
|
|
194
|
+
out = out.split(`"${placeholder}"`).join(`"${value}"`);
|
|
195
|
+
}
|
|
196
|
+
return out;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Render ONE secret template. Reads the template YAML from `templatesRoot`
|
|
200
|
+
* (resolved by the caller to either source or install-mode location),
|
|
201
|
+
* resolves each placeholder, persists generated values to host files,
|
|
202
|
+
* substitutes, and returns the rendered YAML.
|
|
203
|
+
*
|
|
204
|
+
* `reuse` carries the prior state's per-key values when an unrotated
|
|
205
|
+
* re-apply is requested; null means "this is the first apply or operator
|
|
206
|
+
* asked for rotation."
|
|
207
|
+
*/
|
|
208
|
+
export function renderOneSecret(binding, templatesRoot, reuse, deps = {}, rotate = false) {
|
|
209
|
+
const olamHome = deps.olamHome ?? OLAM_HOME;
|
|
210
|
+
const readFile = deps.readFile ?? defaultReadFile;
|
|
211
|
+
const writeFile = deps.writeFile ?? defaultWriteFile;
|
|
212
|
+
const fileExists = deps.fileExists ?? defaultFileExists;
|
|
213
|
+
const genRandomHex = deps.genRandomHex ?? defaultGenRandomHex;
|
|
214
|
+
const runGhTokenCmd = deps.runGhTokenCmd ?? defaultRunGhTokenCmd;
|
|
215
|
+
const templatePath = join(templatesRoot, binding.templateRelPath);
|
|
216
|
+
const templateYaml = readFile(templatePath, 'utf8');
|
|
217
|
+
const keys = {};
|
|
218
|
+
const placeholderToValue = {};
|
|
219
|
+
const missingSources = [];
|
|
220
|
+
for (const p of binding.placeholders) {
|
|
221
|
+
const resolved = resolveSourceValue(p.source, olamHome, reuse?.keys[p.key], rotate, { readFile, writeFile, fileExists, genRandomHex, runGhTokenCmd });
|
|
222
|
+
if (!resolved.ok) {
|
|
223
|
+
missingSources.push(`${p.key}: ${resolved.reason}`);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
keys[p.key] = resolved.value;
|
|
227
|
+
placeholderToValue[p.placeholder] = resolved.value;
|
|
228
|
+
}
|
|
229
|
+
if (missingSources.length > 0) {
|
|
230
|
+
return {
|
|
231
|
+
secretName: binding.secretName,
|
|
232
|
+
status: 'skipped',
|
|
233
|
+
keys,
|
|
234
|
+
missingSources,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
const renderedYaml = substitutePlaceholders(templateYaml, placeholderToValue);
|
|
238
|
+
return {
|
|
239
|
+
secretName: binding.secretName,
|
|
240
|
+
status: 'resolved',
|
|
241
|
+
renderedYaml,
|
|
242
|
+
keys,
|
|
243
|
+
missingSources: [],
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Render the full set of shipped Secret templates. `rotate=true` discards
|
|
248
|
+
* the on-disk state and regenerates every value; `rotate=false` reuses
|
|
249
|
+
* state for unchanged secrets so worlds caching old tokens don't break.
|
|
250
|
+
*/
|
|
251
|
+
export function renderAllSecrets(templatesRoot, opts, deps = {}) {
|
|
252
|
+
const readState = deps.readState ?? (() => readSecretsState(deps.statePath ?? K8S_SECRETS_STATE_PATH));
|
|
253
|
+
const writeState = deps.writeState ?? ((s) => writeSecretsState(s, deps.statePath ?? K8S_SECRETS_STATE_PATH));
|
|
254
|
+
const prior = opts.rotate ? null : readState();
|
|
255
|
+
const results = [];
|
|
256
|
+
const nextSecrets = {};
|
|
257
|
+
for (const binding of SECRET_TEMPLATE_BINDINGS) {
|
|
258
|
+
const reuse = prior?.secrets[binding.secretName] ?? null;
|
|
259
|
+
const r = renderOneSecret(binding, templatesRoot, reuse, deps, opts.rotate);
|
|
260
|
+
results.push(r);
|
|
261
|
+
nextSecrets[binding.secretName] = {
|
|
262
|
+
keys: r.keys,
|
|
263
|
+
...(r.missingSources.length > 0 ? { skipped: r.missingSources } : {}),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
const nextState = {
|
|
267
|
+
version: 1,
|
|
268
|
+
context: opts.context,
|
|
269
|
+
namespace: opts.namespace,
|
|
270
|
+
generatedAt: new Date().toISOString(),
|
|
271
|
+
secrets: nextSecrets,
|
|
272
|
+
};
|
|
273
|
+
// Only persist when at least one secret resolved — avoids an empty state file
|
|
274
|
+
// on a wholly-failed first run (where every gh-token source was absent and
|
|
275
|
+
// the operator hadn't yet generated `~/.olam/*` files).
|
|
276
|
+
if (results.some((r) => r.status === 'resolved')) {
|
|
277
|
+
writeState(nextState);
|
|
278
|
+
}
|
|
279
|
+
return { results, nextState };
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=k8s-secret-render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"k8s-secret-render.js","sourceRoot":"","sources":["../../src/lib/k8s-secret-render.ts"],"names":[],"mappings":"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"}
|