@cat-factory/integrations 0.163.1 → 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.
- package/dist/modules/environments/EnvironmentProvisioningService.js +1 -1
- package/dist/modules/environments/EnvironmentProvisioningService.js.map +1 -1
- package/dist/modules/kubernetes/KubernetesEnvironmentProvider.d.ts +59 -0
- package/dist/modules/kubernetes/KubernetesEnvironmentProvider.d.ts.map +1 -1
- package/dist/modules/kubernetes/KubernetesEnvironmentProvider.js +171 -5
- package/dist/modules/kubernetes/KubernetesEnvironmentProvider.js.map +1 -1
- package/dist/modules/kubernetes/kubernetes-deploy.logic.d.ts +16 -0
- package/dist/modules/kubernetes/kubernetes-deploy.logic.d.ts.map +1 -1
- package/dist/modules/kubernetes/kubernetes-deploy.logic.js +20 -1
- package/dist/modules/kubernetes/kubernetes-deploy.logic.js.map +1 -1
- package/dist/modules/kubernetes/kubernetes-registry-auth.logic.d.ts +240 -0
- package/dist/modules/kubernetes/kubernetes-registry-auth.logic.d.ts.map +1 -0
- package/dist/modules/kubernetes/kubernetes-registry-auth.logic.js +407 -0
- package/dist/modules/kubernetes/kubernetes-registry-auth.logic.js.map +1 -0
- package/package.json +4 -4
|
@@ -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.
|
|
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.
|
|
32
|
-
"@cat-factory/kernel": "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.
|
|
38
|
+
"@cat-factory/caching": "0.20.25"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsc -b tsconfig.build.json",
|