@cat-factory/integrations 0.163.0 → 0.164.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.
@@ -0,0 +1,407 @@
1
+ import { isLocalMachineHost } from '@cat-factory/kernel';
2
+ import { resolveImageOverrides } from './kubernetes-deploy.logic.js';
3
+ // Pure helpers for wiring a per-PR namespace's REGISTRY PULL CREDENTIAL on a throwaway
4
+ // local cluster. No I/O — the provider does the apiserver writes.
5
+ //
6
+ // The problem these solve: a per-PR namespace is created by the platform seconds before the
7
+ // manifests are applied, so a pull secret cannot be waiting in it, and the images a scaffolded
8
+ // service builds are published to the VCS host's own registry (GHCR for GitHub, the GitLab
9
+ // container registry for GitLab), which is PRIVATE until somebody makes it public. The pull then
10
+ // 403s for the whole life of the environment and presents as a cluster that never becomes ready.
11
+ //
12
+ // The credential to fix it is already in hand: a provision resolves a short-lived git token to
13
+ // clone the manifests repo, and that same token authenticates against the VCS host's registry.
14
+ // So nothing new is configured, asked for, or stored. What is derived here is only whether the
15
+ // token PLAUSIBLY covers the registry the image names, which is the one judgement that must not
16
+ // be made by guessing.
17
+ /**
18
+ * The apiserver's hostname, or null when the URL will not parse (a provision that is about to
19
+ * fail on its own terms, with a better message than anything derived here).
20
+ */
21
+ export function apiServerHostname(config) {
22
+ try {
23
+ return new URL(config.apiServerUrl).hostname.toLowerCase();
24
+ }
25
+ catch {
26
+ // silent-catch-ok: the provision fails on the malformed URL a moment later. Reported by the
27
+ // caller as an unreadable apiserver rather than swallowed.
28
+ return null;
29
+ }
30
+ }
31
+ /**
32
+ * Whether this cluster is one running on the machine that is provisioning into it, which is what
33
+ * makes the automatic credential wiring appropriate.
34
+ *
35
+ * The test is the APISERVER HOST, and it is deliberately not the handler's declared engine. A
36
+ * public-API caller cannot pick between `local-k3s` and `remote-kubernetes` (the split is not a
37
+ * public fact, and every API-registered connection lands on the remote name), so gating on the
38
+ * engine would give two identically-configured clusters different behaviour depending on which
39
+ * door connected them, and would silently miss the acceptance suite entirely. The apiserver
40
+ * address is the same fact for both doors.
41
+ *
42
+ * `isLocalMachineHost` rather than a bare loopback test, because the two spellings a local
43
+ * cluster actually writes into a kubeconfig are k3d's wildcard `0.0.0.0` and Docker Desktop's
44
+ * `kubernetes.docker.internal`. Gating on loopback alone withheld the whole behaviour from the
45
+ * default setup of both, which is most of the population it exists for. Still not "private"
46
+ * (`isBlockedPrivateHost`): a shared staging cluster on 10.x is somebody else's machine however
47
+ * private its address, and its namespaces are not a place to put a developer's git token unasked.
48
+ * The residual case is a port-forward of a remote apiserver to localhost, which reads as local
49
+ * here; the cost is a short-lived token in a namespace this platform created, and it is named in
50
+ * the operator docs.
51
+ */
52
+ export function isLocalThrowawayCluster(config) {
53
+ const host = apiServerHostname(config);
54
+ return host !== null && isLocalMachineHost(host);
55
+ }
56
+ /** Name of the pull Secret the provider materializes in the per-PR namespace. */
57
+ export const REGISTRY_AUTH_SECRET_NAME = 'cat-factory-registry-auth';
58
+ /**
59
+ * Field manager for the writes this module's resources go in under, DELIBERATELY distinct from
60
+ * the manifest apply's.
61
+ *
62
+ * Server-side apply treats every apply from one manager as that manager's complete desired
63
+ * state, so writing the Secret and the undeclared-account patches under the manifests' manager
64
+ * would have the next manifest apply declare them gone. A separate manager owns them outright.
65
+ *
66
+ * What this does NOT buy is co-ownership of a field the manifests also set: see
67
+ * {@link withPullSecretOnServiceAccounts} for why a DECLARED account's `imagePullSecrets` cannot
68
+ * be split across two managers at all.
69
+ */
70
+ export const REGISTRY_AUTH_FIELD_MANAGER = 'cat-factory-registry-auth';
71
+ /**
72
+ * How long the wired credential stays usable, stated because nothing renews it.
73
+ *
74
+ * The password is the provision's own short-lived git token (a GitHub App installation token
75
+ * lasts about an hour), and the Secret keeps whatever value it held at provision time. Every
76
+ * image pull inside that window succeeds; a pull AFTER it (a rollout, a scale-up, a pod
77
+ * rescheduled onto a node with no cached layer) re-enters `ImagePullBackOff` with no new
78
+ * provisioning-log entry to explain it, because nothing ran. Re-provisioning the environment
79
+ * mints a fresh token and rewrites the Secret.
80
+ *
81
+ * A renewing design would need something that outlives the provision (a controller in the
82
+ * cluster, or a sweep re-writing every live environment's Secret), which is a large amount of
83
+ * machinery for a throwaway local preview whose images are almost always pulled once at rollout.
84
+ * The honest half of the trade is that the window is NAMED: in the recorded step, and in the
85
+ * operator doc.
86
+ */
87
+ export const REGISTRY_AUTH_CREDENTIAL_LIFETIME = 'about an hour, the git token being short-lived and nothing renewing it: a pull after that ' +
88
+ '(a rollout, a scale-up, a reschedule) needs the environment re-provisioned';
89
+ /**
90
+ * The registry host an image reference pulls from, or null when it names none.
91
+ *
92
+ * A reference's first path segment is a registry only when it looks like a host (it carries a
93
+ * `.` or a `:`, or it is `localhost`); otherwise the reference is a Docker Hub short name and
94
+ * this answers null. Null is the honest answer rather than `docker.io`, because the caller's
95
+ * question is "is this a registry a VCS token could cover", and Docker Hub never is.
96
+ *
97
+ * An empty string answers null too. That is not a degenerate case: an unconfigured
98
+ * `imageTemplate` renders `{{image}}` as the empty string, which is exactly the manifest that
99
+ * used to reach a cluster and fail there.
100
+ */
101
+ export function registryHostForImage(image) {
102
+ const ref = image?.trim();
103
+ if (!ref)
104
+ return null;
105
+ const slash = ref.indexOf('/');
106
+ if (slash === -1)
107
+ return null;
108
+ const head = ref.slice(0, slash);
109
+ if (head === 'localhost' || head.includes('.') || head.includes(':'))
110
+ return head;
111
+ return null;
112
+ }
113
+ /**
114
+ * The registry hosts a git credential for `cloneUrl` can be expected to authenticate against.
115
+ *
116
+ * The two hosted providers publish their registry on a DIFFERENT host from their git host, so
117
+ * those two are mapped explicitly. Any other host maps to ITSELF, which is the self-hosted
118
+ * GitLab shape (git and registry on one host). A host whose registry is neither is simply not
119
+ * matched, and an unmatched registry means the convenience does not apply: the failure direction
120
+ * is "no pull secret written", never "a credential sent somewhere it does not belong".
121
+ */
122
+ export function registriesCoveredByCloneUrl(cloneUrl) {
123
+ let host;
124
+ try {
125
+ host = new URL(cloneUrl).hostname.toLowerCase();
126
+ }
127
+ catch {
128
+ // silent-catch-ok: an unparseable clone URL only costs the pull-secret convenience, and the
129
+ // provision goes on to fail (or succeed) on its own terms. There is nothing to report that
130
+ // the clone itself will not report better.
131
+ return [];
132
+ }
133
+ if (!host)
134
+ return [];
135
+ if (host === 'github.com')
136
+ return ['ghcr.io'];
137
+ if (host === 'gitlab.com')
138
+ return ['registry.gitlab.com'];
139
+ return [host];
140
+ }
141
+ /**
142
+ * Every image reference a provision could pull, across both render paths: the single rendered
143
+ * `{{image}}` the raw path substitutes, plus the `newName` of each structured image override a
144
+ * kustomize overlay carries. Both are collected unconditionally because a config may carry
145
+ * either or both, and a candidate that names no registry is dropped downstream anyway.
146
+ */
147
+ export function registryAuthImageCandidates(config, vars) {
148
+ return [vars.image, ...resolveImageOverrides(config.images, vars).map((image) => image.newName)];
149
+ }
150
+ /**
151
+ * The distinct registries a provision's image references name, in first-seen order. An image
152
+ * that names none (a Docker Hub short name, an unset `{{image}}`) contributes nothing.
153
+ */
154
+ export function registriesNamedByImages(images) {
155
+ const seen = new Map();
156
+ for (const image of images) {
157
+ const registry = registryHostForImage(image);
158
+ if (registry && !seen.has(registry.toLowerCase()))
159
+ seen.set(registry.toLowerCase(), registry);
160
+ }
161
+ return [...seen.values()];
162
+ }
163
+ /**
164
+ * Which of a provision's image references the git credential covers, as one credential per
165
+ * distinct registry, or the reason there is none.
166
+ *
167
+ * It takes the image refs as a LIST because the two render paths name their images differently:
168
+ * the raw path resolves one `{{image}}`, while a kustomize overlay carries a structured image
169
+ * override per container. Reducing them here keeps that difference out of the provider.
170
+ *
171
+ * The username is the repo owner when the provision knows one. GHCR and the GitLab registry both
172
+ * authenticate on the TOKEN and accept any non-empty username, so the owner is a readable
173
+ * default rather than a load-bearing value, and the fallback covers a provision that carries no
174
+ * repo context.
175
+ */
176
+ export function resolveRegistryAuth(args) {
177
+ const registries = registriesNamedByImages(args.images);
178
+ if (registries.length === 0) {
179
+ return {
180
+ kind: 'no-registry-image',
181
+ images: args.images.filter((i) => !!i?.trim()),
182
+ };
183
+ }
184
+ if (!args.clone)
185
+ return { kind: 'no-clone-target', registries };
186
+ const token = args.clone.token;
187
+ if (!token)
188
+ return { kind: 'no-token', registries };
189
+ const covered = registriesCoveredByCloneUrl(args.clone.cloneUrl);
190
+ const username = args.repoOwner?.trim() || 'x-access-token';
191
+ const auths = registries
192
+ .filter((registry) => covered.includes(registry.toLowerCase()))
193
+ .map((registry) => ({ registry, username, password: token }));
194
+ if (auths.length === 0)
195
+ return { kind: 'registry-not-covered', registries, covered };
196
+ return { kind: 'wired', auths };
197
+ }
198
+ /** One line for the provisioning log explaining why nothing was attempted. */
199
+ export function describeRegistryAuthSkip(skip) {
200
+ switch (skip.kind) {
201
+ case 'not-local-cluster':
202
+ return (`No registry credential wired: the apiserver ` +
203
+ `(${skip.apiServerHost ?? 'an unreadable URL'}) is not on this machine, and a git ` +
204
+ `credential is only placed into namespaces on a local throwaway cluster. A private ` +
205
+ `image needs a pull secret this deployment provides.`);
206
+ case 'namespace-not-derivable':
207
+ return (`No registry credential wired: this kustomize overlay declares its own namespace, ` +
208
+ `which is resolved when the overlay is built inside the deploy container, so there is ` +
209
+ `no namespace to place a pull secret in before dispatch. Set a namespace template on ` +
210
+ `the connection to get per-PR isolation and automatic registry credentials.`);
211
+ default:
212
+ return exhaustiveSkip(skip);
213
+ }
214
+ }
215
+ /**
216
+ * One line for the provisioning log describing the credential outcome.
217
+ *
218
+ * Every branch is spelled out rather than collapsed into "no credential wired", because the four
219
+ * causes need four different actions and only this log records which one happened: by the time
220
+ * an `ImagePullBackOff` is visible, nothing distinguishes a public image that never needed a
221
+ * credential from a private one whose credential was refused.
222
+ */
223
+ export function describeRegistryAuthVerdict(verdict, report) {
224
+ switch (verdict.kind) {
225
+ case 'wired': {
226
+ const wired = report ?? {
227
+ auths: verdict.auths,
228
+ patchedAccounts: [],
229
+ declaredAccounts: [],
230
+ manifestsVisible: false,
231
+ };
232
+ const covered = wired.manifestsVisible
233
+ ? `${wired.patchedAccounts.length} service account(s) directly and ` +
234
+ `${wired.declaredAccounts.length} declared by the manifests`
235
+ : `the default service account only (this render path builds its manifests inside the ` +
236
+ `deploy container, so the accounts they declare are not visible here)`;
237
+ return (`Wired a ${wired.auths.map((a) => a.registry).join(', ')} pull credential ` +
238
+ `(user '${wired.auths[0].username}') into ${covered}. It lasts ` +
239
+ `${REGISTRY_AUTH_CREDENTIAL_LIFETIME}. A 403 before then means the git token lacks ` +
240
+ `package-read scope.`);
241
+ }
242
+ case 'no-registry-image':
243
+ return (`No registry credential wired: ` +
244
+ `${verdict.images.length > 0 ? `[${verdict.images.join(', ')}] names` : 'the manifests name'} ` +
245
+ `no registry host, so no credential could apply. An image pulled from Docker Hub or an ` +
246
+ `unqualified name is never covered by a git credential.`);
247
+ case 'no-clone-target':
248
+ return (`No registry credential wired for [${verdict.registries.join(', ')}]: this provision has ` +
249
+ `no clone target, so there is no git credential to derive one from (no VCS connection, ` +
250
+ `or a block-less manual provision). A private image will fail to pull.`);
251
+ case 'no-token':
252
+ return (`No registry credential wired for [${verdict.registries.join(', ')}]: the manifests repo ` +
253
+ `is cloned without a token, so none is available to reuse. That is expected for a public ` +
254
+ `manifests repo. A private image will fail to pull.`);
255
+ case 'registry-not-covered':
256
+ return (`No registry credential wired: the images pull from ` +
257
+ `[${verdict.registries.join(', ')}], and the git credential authenticates against ` +
258
+ `[${verdict.covered.join(', ') || 'no registry'}]. A private image will fail to pull.`);
259
+ default:
260
+ return exhaustiveVerdict(verdict);
261
+ }
262
+ }
263
+ /* c8 ignore start -- compile-time totality guards; unreachable while the unions are exhaustive. */
264
+ function exhaustiveSkip(skip) {
265
+ return `No registry credential wired: ${JSON.stringify(skip)}`;
266
+ }
267
+ function exhaustiveVerdict(verdict) {
268
+ return `No registry credential wired: ${JSON.stringify(verdict)}`;
269
+ }
270
+ /* c8 ignore stop */
271
+ /** The `.dockerconfigjson` payload a `kubernetes.io/dockerconfigjson` Secret carries. */
272
+ export function dockerConfigJson(auths) {
273
+ const entries = auths.map((auth) => [
274
+ auth.registry,
275
+ {
276
+ username: auth.username,
277
+ password: auth.password,
278
+ auth: base64(`${auth.username}:${auth.password}`),
279
+ },
280
+ ]);
281
+ return JSON.stringify({ auths: Object.fromEntries(entries) });
282
+ }
283
+ /** The pull Secret to apply into the per-PR namespace. */
284
+ export function buildPullSecret(namespace, auths) {
285
+ return {
286
+ apiVersion: 'v1',
287
+ kind: 'Secret',
288
+ metadata: { name: REGISTRY_AUTH_SECRET_NAME, namespace },
289
+ type: 'kubernetes.io/dockerconfigjson',
290
+ data: { '.dockerconfigjson': base64(dockerConfigJson(auths)) },
291
+ };
292
+ }
293
+ /**
294
+ * The ServiceAccount body that attaches the pull secret to an account the manifests do NOT
295
+ * declare. Applied under the dedicated field manager, carrying `imagePullSecrets` and nothing
296
+ * else, so whatever else the account has is owned by another manager and left alone.
297
+ *
298
+ * Only safe for an UNDECLARED account, which is why {@link serviceAccountsNeedingOwnPatch} picks
299
+ * the names: `imagePullSecrets` is an ATOMIC list, so the manager that applies it last owns the
300
+ * whole thing. Against an account the manifests also apply, this write is either erased by them
301
+ * or erases theirs, depending on the order. A declared account is served by
302
+ * {@link withPullSecretOnServiceAccounts} instead, which puts the entry into the manifests' own
303
+ * body so ONE manager declares the union.
304
+ */
305
+ export function buildServiceAccountPullSecretPatch(name, namespace) {
306
+ return {
307
+ apiVersion: 'v1',
308
+ kind: 'ServiceAccount',
309
+ metadata: { name, namespace },
310
+ imagePullSecrets: [{ name: REGISTRY_AUTH_SECRET_NAME }],
311
+ };
312
+ }
313
+ /** The `imagePullSecrets` entries a ServiceAccount resource already declares, names only. */
314
+ function declaredPullSecretNames(resource) {
315
+ const raw = resource.imagePullSecrets;
316
+ if (!Array.isArray(raw))
317
+ return [];
318
+ return raw
319
+ .map((entry) => entry?.name)
320
+ .filter((name) => typeof name === 'string' && name.trim().length > 0);
321
+ }
322
+ /**
323
+ * The manifests' own resources, with every ServiceAccount they declare carrying the pull secret
324
+ * alongside whatever it already declared.
325
+ *
326
+ * This exists because `ServiceAccount.imagePullSecrets` is an ATOMIC list in the OpenAPI schema:
327
+ * server-side apply gives ownership of the WHOLE list to one manager, so two managers cannot own
328
+ * disjoint entries of it the way they can own disjoint fields. A separate-manager patch beside
329
+ * the manifests' own apply is therefore not a merge, it is a race, and with `force=true` the
330
+ * later writer wins outright. Since the manifests are applied by this provider, the fix is to
331
+ * apply the UNION: the entry goes into their declared body and no second writer touches the
332
+ * field. Their own entries are preserved and never reordered, so a manifest that pins a pull
333
+ * secret keeps it first.
334
+ *
335
+ * A pod's pull secrets are resolved by the ServiceAccount admission controller when the pod is
336
+ * CREATED, which is the other half of why this shape is the correct one: patching the account
337
+ * after its Deployment applied would leave the pods already admitted without the secret.
338
+ */
339
+ export function withPullSecretOnServiceAccounts(resources) {
340
+ return resources.map((resource) => {
341
+ if (resource.kind !== 'ServiceAccount')
342
+ return resource;
343
+ const existing = declaredPullSecretNames(resource);
344
+ if (existing.includes(REGISTRY_AUTH_SECRET_NAME))
345
+ return resource;
346
+ return {
347
+ ...resource,
348
+ imagePullSecrets: [
349
+ ...existing.map((name) => ({ name })),
350
+ { name: REGISTRY_AUTH_SECRET_NAME },
351
+ ],
352
+ };
353
+ });
354
+ }
355
+ /**
356
+ * The ServiceAccounts that need a patch of THIS provider's own: `default` plus every account a
357
+ * pod template NAMES, minus the ones the manifests declare themselves.
358
+ *
359
+ * The subtraction is the point. A declared account gets the secret through
360
+ * {@link withPullSecretOnServiceAccounts}, inside the manifests' own apply; patching it here as
361
+ * well would be the atomic-list race that function exists to avoid. What is left is exactly the
362
+ * set nothing else writes: `default`, which Kubernetes creates per namespace, and any account a
363
+ * workload names but never defines (a cluster-wide account, or a manifest that assumes one).
364
+ */
365
+ export function serviceAccountsNeedingOwnPatch(resources) {
366
+ const declared = new Set();
367
+ const needed = new Set(['default']);
368
+ for (const resource of resources) {
369
+ if (resource.kind === 'ServiceAccount' && resource.metadata.name) {
370
+ declared.add(resource.metadata.name);
371
+ }
372
+ const referenced = podSpecServiceAccountName(resource);
373
+ if (referenced)
374
+ needed.add(referenced);
375
+ }
376
+ return [...needed].filter((name) => !declared.has(name));
377
+ }
378
+ /**
379
+ * The `serviceAccountName` a resource's pod template declares.
380
+ *
381
+ * Three nestings reach a pod spec and all three occur in ordinary manifests: a bare Pod's own
382
+ * `spec`, a workload's `spec.template.spec` (Deployment, StatefulSet, DaemonSet, Job), and a
383
+ * CronJob's `spec.jobTemplate.spec.template.spec`. Missing the last one left a CronJob naming an
384
+ * undeclared account with no pull secret, which is the failure mode that shows up hours later on
385
+ * the first schedule rather than at provision time. Unknown shapes answer null.
386
+ */
387
+ function podSpecServiceAccountName(resource) {
388
+ const spec = resource.spec;
389
+ const name = spec?.jobTemplate?.spec?.template?.spec?.serviceAccountName ??
390
+ spec?.template?.spec?.serviceAccountName ??
391
+ spec?.serviceAccountName;
392
+ return typeof name === 'string' && name.trim() ? name.trim() : null;
393
+ }
394
+ /**
395
+ * Base64 of a string's UTF-8 bytes. `btoa` alone is wrong here: it encodes CODE UNITS, so any
396
+ * non-ASCII character in a repo owner would produce a payload the apiserver stores and the
397
+ * kubelet cannot parse. The bytes are accumulated in a loop rather than spread into
398
+ * `String.fromCharCode(...)`, which blows the call stack on a large enough input.
399
+ */
400
+ function base64(value) {
401
+ const bytes = new TextEncoder().encode(value);
402
+ let binary = '';
403
+ for (const byte of bytes)
404
+ binary += String.fromCharCode(byte);
405
+ return btoa(binary);
406
+ }
407
+ //# sourceMappingURL=kubernetes-registry-auth.logic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kubernetes-registry-auth.logic.js","sourceRoot":"","sources":["../../../src/modules/kubernetes/kubernetes-registry-auth.logic.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/integrations",
3
- "version": "0.163.0",
3
+ "version": "0.164.0",
4
4
  "description": "External-system integration domain logic for the Agent Architecture Board (GitHub, documents, tasks, environments, runners).",
5
5
  "repository": {
6
6
  "type": "git",
@@ -28,14 +28,14 @@
28
28
  "p-map": "^7.0.6",
29
29
  "undici": "^8.10.0",
30
30
  "yaml": "^2.9.0",
31
- "@cat-factory/contracts": "0.314.0",
32
- "@cat-factory/kernel": "0.302.0"
31
+ "@cat-factory/contracts": "0.316.0",
32
+ "@cat-factory/kernel": "0.304.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "typescript": "7.0.2",
36
36
  "valibot": "^1.4.2",
37
37
  "vitest": "^4.1.10",
38
- "@cat-factory/caching": "0.20.23"
38
+ "@cat-factory/caching": "0.20.25"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsc -b tsconfig.build.json",