log10x-mcp 1.12.6 → 1.12.7
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/build/lib/configure-engine/kubectl-writer.d.ts +177 -0
- package/build/lib/configure-engine/kubectl-writer.js +423 -0
- package/build/lib/configure-engine/kubectl-writer.js.map +1 -0
- package/build/lib/slot-naming.d.ts +73 -0
- package/build/lib/slot-naming.js +526 -0
- package/build/lib/slot-naming.js.map +1 -0
- package/build/lib/validate-runner.d.ts +97 -0
- package/build/lib/validate-runner.js +242 -0
- package/build/lib/validate-runner.js.map +1 -0
- package/build/tools/configure-engine.d.ts +18 -1
- package/build/tools/configure-engine.js +18 -13
- package/build/tools/configure-engine.js.map +1 -1
- package/build/tools/validate.d.ts +42 -0
- package/build/tools/validate.js +140 -0
- package/build/tools/validate.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kubectl-writer — push a ConfigMap to the customer's k8s cluster via
|
|
3
|
+
* `kubectl apply`.
|
|
4
|
+
*
|
|
5
|
+
* Companion to the existing gitops/gh writer in configure-engine.ts. The
|
|
6
|
+
* gh path opens a PR against the customer's gitops repo (slow loop:
|
|
7
|
+
* review → merge → poll → engine hot-reload). This path is the
|
|
8
|
+
* direct-apply alternative for environments where:
|
|
9
|
+
*
|
|
10
|
+
* - the engine pulls action-intent from a ConfigMap (new pull source
|
|
11
|
+
* introduced alongside this writer), and
|
|
12
|
+
* - the operator wants in-session iteration speed (~seconds vs
|
|
13
|
+
* minutes), and
|
|
14
|
+
* - the MCP has direct kubectl access (the user's local kubeconfig
|
|
15
|
+
* pointed at the target cluster).
|
|
16
|
+
*
|
|
17
|
+
* Architecture: out-of-cluster. The MCP runs on the user's Mac and
|
|
18
|
+
* invokes `kubectl apply -f - --dry-run=server`. The ConfigMap YAML is
|
|
19
|
+
* built inline from caller-supplied content and piped via stdin.
|
|
20
|
+
*
|
|
21
|
+
* Safety model (layered):
|
|
22
|
+
* 1. Caller flag (auto_apply=false / read_only=true on the tool) short-
|
|
23
|
+
* circuits to dry-run; the rendered YAML is returned in-envelope.
|
|
24
|
+
* 2. Even when auto-applying, we ALWAYS do a `--dry-run=server` pass
|
|
25
|
+
* first to catch RBAC/quota/admission errors without writing.
|
|
26
|
+
* 3. All user-controlled strings reach `kubectl` via process args
|
|
27
|
+
* (spawnSync's argv array) — NEVER via shell interpolation. The YAML
|
|
28
|
+
* body itself is piped through stdin, not constructed in argv.
|
|
29
|
+
* 4. ConfigMap-size guard: k8s rejects objects > 1 MiB at the apiserver.
|
|
30
|
+
* We pre-flight on the SUM of value sizes and return a structured
|
|
31
|
+
* `request_entity_too_large` error before kubectl is invoked.
|
|
32
|
+
*
|
|
33
|
+
* Failure-classification: stderr from kubectl is regex-matched against
|
|
34
|
+
* the common error families so the agent can branch on `error_type`
|
|
35
|
+
* (forbidden, not_found, request_entity_too_large, conflict, timeout,
|
|
36
|
+
* unknown). All errors flow back as `{ ok: false, error: {...} }` —
|
|
37
|
+
* the function never throws.
|
|
38
|
+
*
|
|
39
|
+
* Why server-side dry-run by default: client-side dry-run only checks
|
|
40
|
+
* schema; server-side runs the admission chain (RBAC, ResourceQuota,
|
|
41
|
+
* webhooks). The latter is what the operator actually needs to know
|
|
42
|
+
* about. If the cluster doesn't support server-side dry-run we fall
|
|
43
|
+
* back to client-side and warn.
|
|
44
|
+
*/
|
|
45
|
+
import type { PrimitiveError } from '../primitive-errors.js';
|
|
46
|
+
/**
|
|
47
|
+
* The k8s ConfigMap size limit is 1 MiB (apiserver enforcement). Allow
|
|
48
|
+
* a small headroom so the metadata block doesn't push us over.
|
|
49
|
+
*/
|
|
50
|
+
export declare const CONFIGMAP_MAX_BYTES: number;
|
|
51
|
+
export interface KubectlWriterArgs {
|
|
52
|
+
/** Target k8s namespace. Must be DNS-1123-label valid. */
|
|
53
|
+
namespace: string;
|
|
54
|
+
/** Target ConfigMap name. Must be DNS-1123-subdomain valid. */
|
|
55
|
+
configmap: string;
|
|
56
|
+
/**
|
|
57
|
+
* ConfigMap `data` map. Each value is a string; binary values are not
|
|
58
|
+
* supported by this writer (the engine pulls action-intent.json which is
|
|
59
|
+
* UTF-8 text).
|
|
60
|
+
*/
|
|
61
|
+
content: {
|
|
62
|
+
[key: string]: string;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* When true, skip the real apply and only run `--dry-run=server`. The
|
|
66
|
+
* returned `dry_run_diff` carries kubectl's stdout describing what
|
|
67
|
+
* WOULD change. When false, we still run server-side dry-run first as
|
|
68
|
+
* a pre-flight, then do the real apply if the dry-run succeeded.
|
|
69
|
+
*/
|
|
70
|
+
dryRun: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Optional labels to merge into the ConfigMap metadata. Useful for
|
|
73
|
+
* GitOps reconciliation markers (`app.kubernetes.io/managed-by=log10x`)
|
|
74
|
+
* and for the engine's pull-source label selector.
|
|
75
|
+
*/
|
|
76
|
+
labels?: {
|
|
77
|
+
[key: string]: string;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Optional annotations. Stamped with `log10x.com/written-at` so the
|
|
81
|
+
* engine can log the freshness of the ConfigMap on each pull.
|
|
82
|
+
*/
|
|
83
|
+
annotations?: {
|
|
84
|
+
[key: string]: string;
|
|
85
|
+
};
|
|
86
|
+
/** Override the default timeout (ms). Tests pass a short value. */
|
|
87
|
+
timeoutMs?: number;
|
|
88
|
+
/**
|
|
89
|
+
* Override the kubectl binary path. Default `kubectl` (resolved via
|
|
90
|
+
* PATH). Tests pass `/bin/false` etc.
|
|
91
|
+
*/
|
|
92
|
+
kubectlPath?: string;
|
|
93
|
+
/**
|
|
94
|
+
* Injected spawn implementation. Defaults to node's spawnSync. Tests
|
|
95
|
+
* pass a mock that returns canned stdout/stderr/exit codes without
|
|
96
|
+
* starting a real process.
|
|
97
|
+
*/
|
|
98
|
+
spawn?: SpawnSyncFn;
|
|
99
|
+
}
|
|
100
|
+
export interface KubectlWriterResult {
|
|
101
|
+
ok: boolean;
|
|
102
|
+
/** High-level state, branchable by the caller. */
|
|
103
|
+
status: 'applied' | 'dry_run_ok' | 'failed_validation' | 'failed_apply' | 'forbidden' | 'not_found' | 'request_entity_too_large' | 'conflict' | 'timeout' | 'kubectl_unavailable';
|
|
104
|
+
/** Server-side dry-run diff (kubectl stdout) — present on success. */
|
|
105
|
+
dry_run_diff?: string;
|
|
106
|
+
/** Rendered ConfigMap YAML (always present so the caller can persist). */
|
|
107
|
+
rendered_yaml: string;
|
|
108
|
+
/** Structured error envelope, present iff ok=false. */
|
|
109
|
+
error?: PrimitiveError;
|
|
110
|
+
/**
|
|
111
|
+
* Suggested kubectl one-liner the user can run to confirm the change
|
|
112
|
+
* landed (`kubectl get configmap -n <ns> <name> -o yaml`).
|
|
113
|
+
*/
|
|
114
|
+
verification_hint: string;
|
|
115
|
+
}
|
|
116
|
+
/** Trimmed surface of node's `spawnSync` return value. */
|
|
117
|
+
export interface SpawnSyncResult {
|
|
118
|
+
status: number | null;
|
|
119
|
+
stdout: string | Buffer;
|
|
120
|
+
stderr: string | Buffer;
|
|
121
|
+
error?: Error & {
|
|
122
|
+
code?: string;
|
|
123
|
+
};
|
|
124
|
+
signal?: NodeJS.Signals | null;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Spawn function signature compatible with `child_process.spawnSync`.
|
|
128
|
+
* Tests pass a mock that returns canned results.
|
|
129
|
+
*/
|
|
130
|
+
export type SpawnSyncFn = (command: string, args: string[], options: {
|
|
131
|
+
input: string;
|
|
132
|
+
timeout: number;
|
|
133
|
+
encoding?: BufferEncoding;
|
|
134
|
+
}) => SpawnSyncResult;
|
|
135
|
+
/**
|
|
136
|
+
* Apply a ConfigMap to the customer's cluster (or dry-run only) via
|
|
137
|
+
* kubectl. Never throws — all failures flow back as `{ ok: false, ... }`.
|
|
138
|
+
*
|
|
139
|
+
* The function does:
|
|
140
|
+
* 1. Validate the namespace + configmap names (DNS-1123).
|
|
141
|
+
* 2. Pre-flight the size against the apiserver's 1 MiB cap.
|
|
142
|
+
* 3. Render the ConfigMap YAML deterministically (sorted keys).
|
|
143
|
+
* 4. Run `kubectl apply -f - --dry-run=server`.
|
|
144
|
+
* 5. If dryRun=false and dry-run succeeded, run the real apply.
|
|
145
|
+
* 6. Classify kubectl exit code + stderr into a structured error.
|
|
146
|
+
*/
|
|
147
|
+
export declare function applyViaKubectl(args: KubectlWriterArgs): KubectlWriterResult;
|
|
148
|
+
interface RenderInput {
|
|
149
|
+
namespace: string;
|
|
150
|
+
configmap: string;
|
|
151
|
+
content: {
|
|
152
|
+
[key: string]: string;
|
|
153
|
+
};
|
|
154
|
+
labels?: {
|
|
155
|
+
[key: string]: string;
|
|
156
|
+
};
|
|
157
|
+
annotations?: {
|
|
158
|
+
[key: string]: string;
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Render the ConfigMap YAML. Public for testing — the format is stable
|
|
163
|
+
* across kubectl versions.
|
|
164
|
+
*/
|
|
165
|
+
export declare function renderConfigMapYaml(input: RenderInput): string;
|
|
166
|
+
/**
|
|
167
|
+
* Map kubectl stderr text to a high-level status code. Exposed so the
|
|
168
|
+
* top-level result can carry both the structured error and a coarse
|
|
169
|
+
* branch the caller can switch on without parsing strings.
|
|
170
|
+
*/
|
|
171
|
+
export declare function classifyKubectlStderr(stderr: string): KubectlWriterResult['status'];
|
|
172
|
+
/**
|
|
173
|
+
* Build the kubectl one-liner the caller surfaces in `verification_hint`
|
|
174
|
+
* so the user can confirm the apply landed.
|
|
175
|
+
*/
|
|
176
|
+
export declare function buildVerificationHint(namespace: string, configmap: string): string;
|
|
177
|
+
export {};
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kubectl-writer — push a ConfigMap to the customer's k8s cluster via
|
|
3
|
+
* `kubectl apply`.
|
|
4
|
+
*
|
|
5
|
+
* Companion to the existing gitops/gh writer in configure-engine.ts. The
|
|
6
|
+
* gh path opens a PR against the customer's gitops repo (slow loop:
|
|
7
|
+
* review → merge → poll → engine hot-reload). This path is the
|
|
8
|
+
* direct-apply alternative for environments where:
|
|
9
|
+
*
|
|
10
|
+
* - the engine pulls action-intent from a ConfigMap (new pull source
|
|
11
|
+
* introduced alongside this writer), and
|
|
12
|
+
* - the operator wants in-session iteration speed (~seconds vs
|
|
13
|
+
* minutes), and
|
|
14
|
+
* - the MCP has direct kubectl access (the user's local kubeconfig
|
|
15
|
+
* pointed at the target cluster).
|
|
16
|
+
*
|
|
17
|
+
* Architecture: out-of-cluster. The MCP runs on the user's Mac and
|
|
18
|
+
* invokes `kubectl apply -f - --dry-run=server`. The ConfigMap YAML is
|
|
19
|
+
* built inline from caller-supplied content and piped via stdin.
|
|
20
|
+
*
|
|
21
|
+
* Safety model (layered):
|
|
22
|
+
* 1. Caller flag (auto_apply=false / read_only=true on the tool) short-
|
|
23
|
+
* circuits to dry-run; the rendered YAML is returned in-envelope.
|
|
24
|
+
* 2. Even when auto-applying, we ALWAYS do a `--dry-run=server` pass
|
|
25
|
+
* first to catch RBAC/quota/admission errors without writing.
|
|
26
|
+
* 3. All user-controlled strings reach `kubectl` via process args
|
|
27
|
+
* (spawnSync's argv array) — NEVER via shell interpolation. The YAML
|
|
28
|
+
* body itself is piped through stdin, not constructed in argv.
|
|
29
|
+
* 4. ConfigMap-size guard: k8s rejects objects > 1 MiB at the apiserver.
|
|
30
|
+
* We pre-flight on the SUM of value sizes and return a structured
|
|
31
|
+
* `request_entity_too_large` error before kubectl is invoked.
|
|
32
|
+
*
|
|
33
|
+
* Failure-classification: stderr from kubectl is regex-matched against
|
|
34
|
+
* the common error families so the agent can branch on `error_type`
|
|
35
|
+
* (forbidden, not_found, request_entity_too_large, conflict, timeout,
|
|
36
|
+
* unknown). All errors flow back as `{ ok: false, error: {...} }` —
|
|
37
|
+
* the function never throws.
|
|
38
|
+
*
|
|
39
|
+
* Why server-side dry-run by default: client-side dry-run only checks
|
|
40
|
+
* schema; server-side runs the admission chain (RBAC, ResourceQuota,
|
|
41
|
+
* webhooks). The latter is what the operator actually needs to know
|
|
42
|
+
* about. If the cluster doesn't support server-side dry-run we fall
|
|
43
|
+
* back to client-side and warn.
|
|
44
|
+
*/
|
|
45
|
+
import { spawnSync } from 'node:child_process';
|
|
46
|
+
// ─── public surface ──────────────────────────────────────────────────
|
|
47
|
+
/**
|
|
48
|
+
* The k8s ConfigMap size limit is 1 MiB (apiserver enforcement). Allow
|
|
49
|
+
* a small headroom so the metadata block doesn't push us over.
|
|
50
|
+
*/
|
|
51
|
+
export const CONFIGMAP_MAX_BYTES = 1024 * 1024;
|
|
52
|
+
const CONFIGMAP_HEADROOM_BYTES = 4 * 1024;
|
|
53
|
+
const CONFIGMAP_SOFT_LIMIT = CONFIGMAP_MAX_BYTES - CONFIGMAP_HEADROOM_BYTES;
|
|
54
|
+
/** Default per-call timeout for kubectl invocations (ms). */
|
|
55
|
+
const KUBECTL_TIMEOUT_MS = 30_000;
|
|
56
|
+
// ─── entry ───────────────────────────────────────────────────────────
|
|
57
|
+
/**
|
|
58
|
+
* Apply a ConfigMap to the customer's cluster (or dry-run only) via
|
|
59
|
+
* kubectl. Never throws — all failures flow back as `{ ok: false, ... }`.
|
|
60
|
+
*
|
|
61
|
+
* The function does:
|
|
62
|
+
* 1. Validate the namespace + configmap names (DNS-1123).
|
|
63
|
+
* 2. Pre-flight the size against the apiserver's 1 MiB cap.
|
|
64
|
+
* 3. Render the ConfigMap YAML deterministically (sorted keys).
|
|
65
|
+
* 4. Run `kubectl apply -f - --dry-run=server`.
|
|
66
|
+
* 5. If dryRun=false and dry-run succeeded, run the real apply.
|
|
67
|
+
* 6. Classify kubectl exit code + stderr into a structured error.
|
|
68
|
+
*/
|
|
69
|
+
export function applyViaKubectl(args) {
|
|
70
|
+
const spawn = args.spawn ?? spawnSync;
|
|
71
|
+
const kubectl = args.kubectlPath ?? 'kubectl';
|
|
72
|
+
const timeoutMs = args.timeoutMs ?? KUBECTL_TIMEOUT_MS;
|
|
73
|
+
const verification_hint = buildVerificationHint(args.namespace, args.configmap);
|
|
74
|
+
// ── 1. Validate identifiers ───────────────────────────────────────
|
|
75
|
+
const nsErr = validateDns1123Label(args.namespace, 'namespace');
|
|
76
|
+
if (nsErr) {
|
|
77
|
+
const yaml = ''; // not rendered when args are invalid
|
|
78
|
+
return {
|
|
79
|
+
ok: false,
|
|
80
|
+
status: 'failed_validation',
|
|
81
|
+
rendered_yaml: yaml,
|
|
82
|
+
error: nsErr,
|
|
83
|
+
verification_hint,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const cmErr = validateDns1123Subdomain(args.configmap, 'configmap');
|
|
87
|
+
if (cmErr) {
|
|
88
|
+
return {
|
|
89
|
+
ok: false,
|
|
90
|
+
status: 'failed_validation',
|
|
91
|
+
rendered_yaml: '',
|
|
92
|
+
error: cmErr,
|
|
93
|
+
verification_hint,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// ── 2. Pre-flight size ────────────────────────────────────────────
|
|
97
|
+
const sizeError = preflightSize(args.content);
|
|
98
|
+
if (sizeError) {
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
status: 'request_entity_too_large',
|
|
102
|
+
rendered_yaml: '',
|
|
103
|
+
error: sizeError,
|
|
104
|
+
verification_hint,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// ── 3. Render YAML deterministically ──────────────────────────────
|
|
108
|
+
const yaml = renderConfigMapYaml({
|
|
109
|
+
namespace: args.namespace,
|
|
110
|
+
configmap: args.configmap,
|
|
111
|
+
content: args.content,
|
|
112
|
+
labels: args.labels,
|
|
113
|
+
annotations: args.annotations,
|
|
114
|
+
});
|
|
115
|
+
// ── 4. Server-side dry-run pre-flight ─────────────────────────────
|
|
116
|
+
const dryRunResult = runKubectl(spawn, kubectl, ['apply', '-n', args.namespace, '-f', '-', '--dry-run=server', '-o', 'yaml'], yaml, timeoutMs);
|
|
117
|
+
if (dryRunResult.kind === 'unavailable') {
|
|
118
|
+
return {
|
|
119
|
+
ok: false,
|
|
120
|
+
status: 'kubectl_unavailable',
|
|
121
|
+
rendered_yaml: yaml,
|
|
122
|
+
error: dryRunResult.error,
|
|
123
|
+
verification_hint,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (dryRunResult.kind === 'timeout') {
|
|
127
|
+
return {
|
|
128
|
+
ok: false,
|
|
129
|
+
status: 'timeout',
|
|
130
|
+
rendered_yaml: yaml,
|
|
131
|
+
error: dryRunResult.error,
|
|
132
|
+
verification_hint,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (dryRunResult.kind === 'error') {
|
|
136
|
+
return {
|
|
137
|
+
ok: false,
|
|
138
|
+
status: classifyKubectlStderr(dryRunResult.stderr),
|
|
139
|
+
rendered_yaml: yaml,
|
|
140
|
+
error: dryRunResult.error,
|
|
141
|
+
verification_hint,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// dry-run succeeded — caller wanted dry-run only, return now.
|
|
145
|
+
if (args.dryRun) {
|
|
146
|
+
return {
|
|
147
|
+
ok: true,
|
|
148
|
+
status: 'dry_run_ok',
|
|
149
|
+
rendered_yaml: yaml,
|
|
150
|
+
dry_run_diff: dryRunResult.stdout,
|
|
151
|
+
verification_hint,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// ── 5. Real apply ─────────────────────────────────────────────────
|
|
155
|
+
const applyResult = runKubectl(spawn, kubectl, ['apply', '-n', args.namespace, '-f', '-'], yaml, timeoutMs);
|
|
156
|
+
if (applyResult.kind === 'unavailable') {
|
|
157
|
+
return {
|
|
158
|
+
ok: false,
|
|
159
|
+
status: 'kubectl_unavailable',
|
|
160
|
+
rendered_yaml: yaml,
|
|
161
|
+
error: applyResult.error,
|
|
162
|
+
verification_hint,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (applyResult.kind === 'timeout') {
|
|
166
|
+
return {
|
|
167
|
+
ok: false,
|
|
168
|
+
status: 'timeout',
|
|
169
|
+
rendered_yaml: yaml,
|
|
170
|
+
error: applyResult.error,
|
|
171
|
+
verification_hint,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (applyResult.kind === 'error') {
|
|
175
|
+
return {
|
|
176
|
+
ok: false,
|
|
177
|
+
status: classifyKubectlStderr(applyResult.stderr),
|
|
178
|
+
rendered_yaml: yaml,
|
|
179
|
+
error: applyResult.error,
|
|
180
|
+
verification_hint,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
ok: true,
|
|
185
|
+
status: 'applied',
|
|
186
|
+
rendered_yaml: yaml,
|
|
187
|
+
dry_run_diff: dryRunResult.stdout,
|
|
188
|
+
verification_hint,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Render the ConfigMap YAML. Public for testing — the format is stable
|
|
193
|
+
* across kubectl versions.
|
|
194
|
+
*/
|
|
195
|
+
export function renderConfigMapYaml(input) {
|
|
196
|
+
const lines = [];
|
|
197
|
+
lines.push('apiVersion: v1');
|
|
198
|
+
lines.push('kind: ConfigMap');
|
|
199
|
+
lines.push('metadata:');
|
|
200
|
+
lines.push(` name: ${yamlScalar(input.configmap)}`);
|
|
201
|
+
lines.push(` namespace: ${yamlScalar(input.namespace)}`);
|
|
202
|
+
// Always stamp managed-by so an operator scanning the cluster can find
|
|
203
|
+
// log10x-managed ConfigMaps. Caller labels merge on top.
|
|
204
|
+
const labels = {
|
|
205
|
+
'app.kubernetes.io/managed-by': 'log10x-mcp',
|
|
206
|
+
'log10x.com/source': 'configure_engine',
|
|
207
|
+
...(input.labels ?? {}),
|
|
208
|
+
};
|
|
209
|
+
lines.push(' labels:');
|
|
210
|
+
for (const k of Object.keys(labels).sort()) {
|
|
211
|
+
lines.push(` ${yamlScalarKey(k)}: ${yamlScalar(labels[k])}`);
|
|
212
|
+
}
|
|
213
|
+
// Always stamp written-at; merge caller annotations on top.
|
|
214
|
+
const annotations = {
|
|
215
|
+
'log10x.com/written-at': new Date().toISOString(),
|
|
216
|
+
...(input.annotations ?? {}),
|
|
217
|
+
};
|
|
218
|
+
lines.push(' annotations:');
|
|
219
|
+
for (const k of Object.keys(annotations).sort()) {
|
|
220
|
+
lines.push(` ${yamlScalarKey(k)}: ${yamlScalar(annotations[k])}`);
|
|
221
|
+
}
|
|
222
|
+
lines.push('data:');
|
|
223
|
+
// Sort keys so the rendered YAML is byte-identical for identical input.
|
|
224
|
+
const sortedKeys = Object.keys(input.content).sort();
|
|
225
|
+
for (const k of sortedKeys) {
|
|
226
|
+
const v = input.content[k];
|
|
227
|
+
lines.push(` ${yamlScalarKey(k)}: ${yamlBlockOrScalar(v, 4)}`);
|
|
228
|
+
}
|
|
229
|
+
return lines.join('\n') + '\n';
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Single-quoted YAML scalar. We escape `'` by doubling it. This is the
|
|
233
|
+
* safe encoding for arbitrary ASCII; multi-line / large values use the
|
|
234
|
+
* block-literal encoding below.
|
|
235
|
+
*/
|
|
236
|
+
function yamlScalar(s) {
|
|
237
|
+
// Quote always so we don't trip YAML 1.1's surprise booleans
|
|
238
|
+
// (yes/no/on/off) or numeric coercions.
|
|
239
|
+
return `'${s.replace(/'/g, "''")}'`;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* YAML key in scalar form. We quote keys that contain non-identifier
|
|
243
|
+
* characters (dots, slashes — common in k8s annotation keys).
|
|
244
|
+
*/
|
|
245
|
+
function yamlScalarKey(k) {
|
|
246
|
+
if (/^[A-Za-z_][A-Za-z0-9_-]*$/.test(k))
|
|
247
|
+
return k;
|
|
248
|
+
return `'${k.replace(/'/g, "''")}'`;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Emit a value as a single-line quoted scalar when it fits, otherwise
|
|
252
|
+
* as a block literal. The block literal preserves newlines exactly.
|
|
253
|
+
*/
|
|
254
|
+
function yamlBlockOrScalar(s, indent) {
|
|
255
|
+
if (!s.includes('\n'))
|
|
256
|
+
return yamlScalar(s);
|
|
257
|
+
const pad = ' '.repeat(indent);
|
|
258
|
+
const body = s.split('\n').map((l) => `${pad}${l}`).join('\n');
|
|
259
|
+
// `|-` strips the trailing newline; `|` keeps one. action-intent.json
|
|
260
|
+
// ends with a single trailing newline, so `|` matches it exactly.
|
|
261
|
+
return `|\n${body}`;
|
|
262
|
+
}
|
|
263
|
+
// ─── validation ──────────────────────────────────────────────────────
|
|
264
|
+
const DNS_1123_LABEL_RE = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/;
|
|
265
|
+
const DNS_1123_SUBDOMAIN_RE = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/;
|
|
266
|
+
function validateDns1123Label(value, what) {
|
|
267
|
+
if (!value) {
|
|
268
|
+
return primitiveError('schema_invalid', `kubectl_configmap delivery: ${what} is empty. Pass a valid DNS-1123 label.`);
|
|
269
|
+
}
|
|
270
|
+
if (value.length > 63) {
|
|
271
|
+
return primitiveError('schema_invalid', `kubectl_configmap delivery: ${what} "${value.slice(0, 30)}…" exceeds the 63-char DNS-1123 label limit.`);
|
|
272
|
+
}
|
|
273
|
+
if (!DNS_1123_LABEL_RE.test(value)) {
|
|
274
|
+
return primitiveError('schema_invalid', `kubectl_configmap delivery: ${what} "${value}" is not a valid DNS-1123 label (lowercase letters, digits, hyphens; must start and end with alphanumeric).`);
|
|
275
|
+
}
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
function validateDns1123Subdomain(value, what) {
|
|
279
|
+
if (!value) {
|
|
280
|
+
return primitiveError('schema_invalid', `kubectl_configmap delivery: ${what} is empty.`);
|
|
281
|
+
}
|
|
282
|
+
if (value.length > 253) {
|
|
283
|
+
return primitiveError('schema_invalid', `kubectl_configmap delivery: ${what} exceeds the 253-char DNS-1123 subdomain limit.`);
|
|
284
|
+
}
|
|
285
|
+
if (!DNS_1123_SUBDOMAIN_RE.test(value)) {
|
|
286
|
+
return primitiveError('schema_invalid', `kubectl_configmap delivery: ${what} "${value}" is not a valid DNS-1123 subdomain.`);
|
|
287
|
+
}
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Pre-flight the ConfigMap size against the apiserver's 1 MiB cap. We
|
|
292
|
+
* sum the byte length of all values plus a generous fudge factor for
|
|
293
|
+
* key names + YAML overhead.
|
|
294
|
+
*/
|
|
295
|
+
function preflightSize(content) {
|
|
296
|
+
let total = 0;
|
|
297
|
+
for (const [k, v] of Object.entries(content)) {
|
|
298
|
+
total += Buffer.byteLength(k, 'utf8');
|
|
299
|
+
total += Buffer.byteLength(v, 'utf8');
|
|
300
|
+
}
|
|
301
|
+
if (total > CONFIGMAP_SOFT_LIMIT) {
|
|
302
|
+
const totalKb = (total / 1024).toFixed(1);
|
|
303
|
+
return {
|
|
304
|
+
error_type: 'schema_invalid',
|
|
305
|
+
retryable: false,
|
|
306
|
+
suggested_backoff_ms: null,
|
|
307
|
+
hint: `ConfigMap payload is ${totalKb} KiB which exceeds the 1 MiB k8s apiserver cap ` +
|
|
308
|
+
`(soft limit ${(CONFIGMAP_SOFT_LIMIT / 1024).toFixed(0)} KiB after headroom). ` +
|
|
309
|
+
`Split the action-intent across multiple ConfigMaps, or compact rare patterns.`,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
return undefined;
|
|
313
|
+
}
|
|
314
|
+
function runKubectl(spawn, kubectl, args, yaml, timeoutMs) {
|
|
315
|
+
let res;
|
|
316
|
+
try {
|
|
317
|
+
res = spawn(kubectl, args, {
|
|
318
|
+
input: yaml,
|
|
319
|
+
timeout: timeoutMs,
|
|
320
|
+
encoding: 'utf8',
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
catch (e) {
|
|
324
|
+
// spawnSync only throws on invalid args; normalize to unavailable.
|
|
325
|
+
return {
|
|
326
|
+
kind: 'unavailable',
|
|
327
|
+
error: primitiveError('config_missing', `Failed to invoke kubectl: ${e.message}. Confirm kubectl is installed and on PATH.`),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
const stdout = bufToString(res.stdout);
|
|
331
|
+
const stderr = bufToString(res.stderr);
|
|
332
|
+
// ENOENT / ENOTDIR: kubectl missing.
|
|
333
|
+
if (res.error && (res.error.code === 'ENOENT' || res.error.code === 'ENOTDIR')) {
|
|
334
|
+
return {
|
|
335
|
+
kind: 'unavailable',
|
|
336
|
+
error: primitiveError('config_missing', `kubectl binary not found (${res.error.code}). Install kubectl and ensure it is on PATH for the MCP process.`),
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
// Timeout via signal SIGTERM and null status (node's spawnSync sets
|
|
340
|
+
// status=null when killed by signal/timeout).
|
|
341
|
+
if (res.signal === 'SIGTERM' && res.status === null) {
|
|
342
|
+
return {
|
|
343
|
+
kind: 'timeout',
|
|
344
|
+
error: primitiveError('backend_timeout', `kubectl timed out after ${timeoutMs}ms. The cluster apiserver may be unreachable; check kubeconfig context and network.`, 2000, true),
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
if (res.status === 0) {
|
|
348
|
+
return { kind: 'ok', stdout, stderr };
|
|
349
|
+
}
|
|
350
|
+
const tail = stderr.trim().split('\n').slice(-5).join('\n').slice(0, 600);
|
|
351
|
+
return {
|
|
352
|
+
kind: 'error',
|
|
353
|
+
stdout,
|
|
354
|
+
stderr,
|
|
355
|
+
error: classifyKubectlError(stderr, res.status, tail),
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function bufToString(b) {
|
|
359
|
+
if (b === undefined || b === null)
|
|
360
|
+
return '';
|
|
361
|
+
return typeof b === 'string' ? b : b.toString('utf8');
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Map kubectl stderr text to a high-level status code. Exposed so the
|
|
365
|
+
* top-level result can carry both the structured error and a coarse
|
|
366
|
+
* branch the caller can switch on without parsing strings.
|
|
367
|
+
*/
|
|
368
|
+
export function classifyKubectlStderr(stderr) {
|
|
369
|
+
const s = stderr.toLowerCase();
|
|
370
|
+
if (/forbidden|access denied|cannot.*resource|user.*cannot/i.test(s))
|
|
371
|
+
return 'forbidden';
|
|
372
|
+
if (/notfound|not found|no such|the server (?:could not find|doesn'?t have)/i.test(s)) {
|
|
373
|
+
return 'not_found';
|
|
374
|
+
}
|
|
375
|
+
if (/requestentitytoolarge|request entity too large|too large/i.test(s)) {
|
|
376
|
+
return 'request_entity_too_large';
|
|
377
|
+
}
|
|
378
|
+
if (/conflict|already exists/i.test(s))
|
|
379
|
+
return 'conflict';
|
|
380
|
+
if (/timeout|timed out|connection refused|unable to connect/i.test(s))
|
|
381
|
+
return 'timeout';
|
|
382
|
+
return 'failed_apply';
|
|
383
|
+
}
|
|
384
|
+
function classifyKubectlError(stderr, status, tail) {
|
|
385
|
+
const cls = classifyKubectlStderr(stderr);
|
|
386
|
+
switch (cls) {
|
|
387
|
+
case 'forbidden':
|
|
388
|
+
return primitiveError('config_missing', `kubectl Forbidden — the kubeconfig user lacks RBAC to write ConfigMaps in this namespace. ` +
|
|
389
|
+
`Bind a Role/RoleBinding granting configmaps create+patch, or switch contexts. kubectl said: ${tail}`);
|
|
390
|
+
case 'not_found':
|
|
391
|
+
return primitiveError('config_missing', `kubectl NotFound — the target namespace does not exist. Create it first ` +
|
|
392
|
+
`(kubectl create namespace …) or pass an existing one. kubectl said: ${tail}`);
|
|
393
|
+
case 'request_entity_too_large':
|
|
394
|
+
return primitiveError('schema_invalid', `kubectl RequestEntityTooLarge — the ConfigMap exceeds the 1 MiB apiserver cap. ` +
|
|
395
|
+
`Split the payload or compact rare patterns. kubectl said: ${tail}`);
|
|
396
|
+
case 'conflict':
|
|
397
|
+
return primitiveError('backend_error', `kubectl Conflict — the resource changed between read and write. Retry the apply. kubectl said: ${tail}`, 1000, true);
|
|
398
|
+
case 'timeout':
|
|
399
|
+
return primitiveError('backend_timeout', `kubectl timed out reaching the cluster. Check kubeconfig context and network. kubectl said: ${tail}`, 2000, true);
|
|
400
|
+
default:
|
|
401
|
+
return primitiveError('backend_error', `kubectl exited ${status ?? 'null'}: ${tail || 'no stderr output'}`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// ─── helpers ─────────────────────────────────────────────────────────
|
|
405
|
+
function primitiveError(error_type, hint, backoff_ms = null, retryable = false) {
|
|
406
|
+
return {
|
|
407
|
+
error_type,
|
|
408
|
+
retryable,
|
|
409
|
+
suggested_backoff_ms: backoff_ms,
|
|
410
|
+
hint,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Build the kubectl one-liner the caller surfaces in `verification_hint`
|
|
415
|
+
* so the user can confirm the apply landed.
|
|
416
|
+
*/
|
|
417
|
+
export function buildVerificationHint(namespace, configmap) {
|
|
418
|
+
// Two-line hint: confirm the ConfigMap, then tail the receiver pods so
|
|
419
|
+
// the user can see the engine pick up the new intent on its next pull.
|
|
420
|
+
return (`kubectl get configmap -n ${namespace} ${configmap} -o yaml | head -40\n` +
|
|
421
|
+
`kubectl logs -n ${namespace} -l app=tenx-fluentd --tail=200 | grep -i 'action-intent\\|configmap'`);
|
|
422
|
+
}
|
|
423
|
+
//# sourceMappingURL=kubectl-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kubectl-writer.js","sourceRoot":"","sources":["../../../src/lib/configure-engine/kubectl-writer.ts"],"names":[],"mappings":"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"}
|