@serve.zone/interfaces 32.58.0 → 32.60.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.
@@ -89,6 +89,7 @@ const serviceRuntimeRefusals = [
89
89
  'network-workload-pool-missing', 'network-workload-pool-exhausted',
90
90
  'volume-driver-unsupported', 'volume-options-unsupported', 'volume-shared-unsupported',
91
91
  'volume-placement-unsupported', 'volume-initialization-unknown', 'volume-ownership-unknown',
92
+ 'launcher-secret-ownership-unknown',
92
93
  'objectstorage-endpoint-missing', 'objectstorage-binding-invalid', 'objectstorage-environment-conflict',
93
94
  'objectstorage-endpoint-unselected', 'database-endpoint-missing', 'database-endpoint-unselected',
94
95
  'published-port-reserved', 'published-port-symmetric-without-egress', 'readiness-network-unsupported',
@@ -128,11 +129,18 @@ export type TServiceRuntimeWaitingReason = typeof serviceRuntimeWaitingReasons[n
128
129
  * `volume-ownership-unknown` a `run-as` ownership under `runAs: null`
129
130
  * (`deriveServiceVolumeClaimInitialization`); neither is defaulted.
130
131
  *
132
+ * `launcher-secret-ownership-unknown` is a service with launcher-environment secrets whose runtime
133
+ * spec runs the image's own user (`runAs: null`) while that user's numeric owner is not known: a
134
+ * named non-root user, a numeric part above the identity bound, a group a `0o440` delivery would grant that the image does not state
135
+ * numerically, or platforms of the pinned index that derive different owners
136
+ * (`deriveLauncherSecretDeliveryOwner`). WorkloadInit could not read secret files owned by a guess.
137
+ *
131
138
  * The object-storage refusals name why a binding's environment cannot be composed
132
139
  * (`composeObjectStorageServiceEnvironment`): `objectstorage-endpoint-missing` a binding whose node
133
140
  * states no valid `corestoreObjectStorage`, `objectstorage-binding-invalid` a binding
134
141
  * `createObjectStorageBindingEnvironment` refuses, and `objectstorage-environment-conflict` a name
135
- * the service's own environment or another binding already sets.
142
+ * the service's own environment, one of its launcher-environment secrets or another binding already
143
+ * sets.
136
144
  *
137
145
  * `database-endpoint-missing` is an enabled database binding placed on a node that states no valid
138
146
  * `corestoreDatabase`: its credential cannot be addressed to any host a workload reaches, so it is
@@ -201,10 +201,17 @@ const findEnvironmentCollisions = (
201
201
  keysArg: readonly string[],
202
202
  ): string[] => keysArg.filter((keyArg) => claimedKeysArg.has(keyArg)).sort();
203
203
 
204
- const describeEnvironmentCollisions = (collisionsArg: readonly string[]): string => (
205
- `objectstorage binding environment collides with ${collisionsArg.join(', ')} already set on the `
206
- + 'workload'
207
- );
204
+ const describeEnvironmentCollisions = (
205
+ collisionsArg: readonly string[],
206
+ secretNamesArg: ReadonlySet<string> = new Set(),
207
+ ): string => {
208
+ const secretCollisions = collisionsArg.filter((keyArg) => secretNamesArg.has(keyArg));
209
+ return `objectstorage binding environment collides with ${collisionsArg.join(', ')} already set on `
210
+ + 'the workload' + (secretCollisions.length > 0
211
+ ? `; ${secretCollisions.join(', ')} reaches it as a launcher-environment secret, which would `
212
+ + 'replace the binding\'s value'
213
+ : '');
214
+ };
208
215
 
209
216
  /**
210
217
  * A workload environment with one binding's environment added.
@@ -252,8 +259,8 @@ export type TObjectStorageEnvironmentWaitingReason = Extract<
252
259
  * `corestoreObjectStorage`; `objectstorage-binding-invalid` a binding
253
260
  * `createObjectStorageBindingEnvironment()` refuses (not `objectstorage`, no canonical bucket, no
254
261
  * canonical retention intent, or evidence that does not prove this binding);
255
- * `objectstorage-environment-conflict` a name the service's own environment or another binding
256
- * already sets.
262
+ * `objectstorage-environment-conflict` a name the service's own environment, one of its
263
+ * launcher-environment secrets or another binding already sets.
257
264
  */
258
265
  export type TObjectStorageEnvironmentRefusal = Extract<
259
266
  TServiceRuntimeRefusal,
@@ -285,16 +292,27 @@ export type TObjectStorageServiceEnvironmentComposition =
285
292
  * already claims all twenty of its names, so a collision with it is refused now, not once its
286
293
  * evidence arrives. The first refusal is answered, otherwise the first wait, otherwise the
287
294
  * composed environment. The inputs are not changed.
295
+ *
296
+ * `secretEnvironmentNamesArg` names the variables the workload receives as launcher-environment
297
+ * secrets (each `ISecretResolvedLauncherEnvironmentDelivery.variableName` of its desired
298
+ * manifest), names only, never a value. The launcher sets a secret over a plain variable of the same name, so a
299
+ * secret named like one of a binding's names would silently replace the binding's value: such a
300
+ * name claims the binding's name exactly as the service's own environment does, and is refused as
301
+ * `objectstorage-environment-conflict`. A secret by any other name is not this composition's
302
+ * concern, and neither are a binding's credential names (`corestoreObjectStorageCredentialSecretKeys`),
303
+ * which the binding itself delivers as secrets and never as plain names.
288
304
  */
289
305
  export const composeObjectStorageServiceEnvironment = async (
290
306
  serviceEnvironmentArg: Readonly<Record<string, string>>,
291
307
  sourcesArg: readonly IObjectStorageBindingEnvironmentSource[],
308
+ secretEnvironmentNamesArg: readonly string[] = [],
292
309
  ): Promise<TObjectStorageServiceEnvironmentComposition> => {
293
310
  const sources = [...sourcesArg].sort((leftArg, rightArg) => (
294
311
  leftArg.binding.id < rightArg.binding.id ? -1 : leftArg.binding.id > rightArg.binding.id ? 1 : 0
295
312
  ));
296
313
  let environment: Record<string, string> = { ...serviceEnvironmentArg };
297
- const claimedKeys = new Set(Object.keys(environment));
314
+ const secretNames: ReadonlySet<string> = new Set(secretEnvironmentNamesArg);
315
+ const claimedKeys = new Set([...Object.keys(environment), ...secretNames]);
298
316
  let wait: Extract<TObjectStorageServiceEnvironmentComposition, { status: 'waiting' }> | null = null;
299
317
  for (const source of sources) {
300
318
  const bindingId = source.binding.id;
@@ -308,7 +326,7 @@ export const composeObjectStorageServiceEnvironment = async (
308
326
  const collisions = findEnvironmentCollisions(claimedKeys, keys);
309
327
  if (collisions.length > 0) {
310
328
  return { status: 'refused', reason: 'objectstorage-environment-conflict', bindingId,
311
- message: describeEnvironmentCollisions(collisions) };
329
+ message: describeEnvironmentCollisions(collisions, secretNames) };
312
330
  }
313
331
  for (const key of keys) claimedKeys.add(key);
314
332
  if (resolution.status === 'composed') {
@@ -12,7 +12,7 @@ export interface IReq_GetRegistryById extends plugins.typedrequestInterfaces.imp
12
12
  id: string;
13
13
  };
14
14
  response: {
15
- registry: data.IExternalRegistry;
15
+ registry: data.IExternalRegistryPublic;
16
16
  };
17
17
  }
18
18
 
@@ -25,7 +25,7 @@ export interface IReq_GetRegistries extends plugins.typedrequestInterfaces.imple
25
25
  identity: userInterfaces.IIdentity;
26
26
  };
27
27
  response: {
28
- registries: data.IExternalRegistry[];
28
+ registries: data.IExternalRegistryPublic[];
29
29
  };
30
30
  }
31
31
 
@@ -39,7 +39,7 @@ export interface IReq_CreateRegistry extends plugins.typedrequestInterfaces.impl
39
39
  registryData: data.IExternalRegistry['data'];
40
40
  };
41
41
  response: {
42
- registry: data.IExternalRegistry;
42
+ registry: data.IExternalRegistryPublic;
43
43
  };
44
44
  }
45
45
 
@@ -51,10 +51,14 @@ export interface IReq_UpdateRegistry extends plugins.typedrequestInterfaces.impl
51
51
  request: {
52
52
  identity: userInterfaces.IIdentity;
53
53
  registryId: string;
54
- registryData: data.IExternalRegistry['data'];
54
+ /**
55
+ * The operator fields to change; a field left out keeps its stored value. `password` is
56
+ * write-only: missing or empty keeps the stored credential, and `authType: 'none'` removes it.
57
+ */
58
+ registryData: Partial<data.TExternalRegistryOperatorData>;
55
59
  };
56
60
  response: {
57
- resultRegistry: data.IExternalRegistry;
61
+ resultRegistry: data.IExternalRegistryPublic;
58
62
  };
59
63
  }
60
64
 
@@ -82,8 +86,10 @@ export interface IReq_VerifyRegistry extends plugins.typedrequestInterfaces.impl
82
86
  registryId: string;
83
87
  };
84
88
  response: {
89
+ /** True exactly for the `authenticated` and `anonymous` outcomes. */
85
90
  success: boolean;
91
+ outcome: data.TExternalRegistryVerifyOutcome;
86
92
  message?: string;
87
- registry?: data.IExternalRegistry;
93
+ registry: data.IExternalRegistryPublic;
88
94
  };
89
95
  }
@@ -455,7 +455,22 @@ extends plugins.typedrequestInterfaces.implementsTR<
455
455
  request: {
456
456
  identity: IIdentity;
457
457
  serviceId: string;
458
- serviceData: TServiceUpdateWritableData;
458
+ /**
459
+ * The fields to change, merged over the stored service one key deep: a key the update omits
460
+ * keeps its stored value, and a key it states replaces the stored value whole (a stated
461
+ * `ports`, `mail` or `domains` is the complete new value, not a patch of the old one).
462
+ * `containerArgs: null` removes the stored argument vector; no other field takes `null`.
463
+ * `registryTarget` is the exception to "replaces": Cloudly derives it from the service and
464
+ * its image version, so a stated one only makes Cloudly derive it again and is never stored.
465
+ * An optional field cannot be cleared yet: a key whose value is `undefined` is dropped in
466
+ * transit, so it reads as omitted (explicit `null` removal for optional fields is a follow-up).
467
+ *
468
+ * Cloudly's guards ask which keys an update states, so send only the fields that change: a
469
+ * service with a runtime spec refuses any update stating `placement` or `scaleFactor`, and an
470
+ * immutable service refuses `imageId`, `imageVersion`, `registryTarget`, `scaleFactor` and
471
+ * `placement`, even when the stated value repeats the stored one.
472
+ */
473
+ serviceData: Partial<TServiceUpdateWritableData>;
459
474
  };
460
475
  response: {
461
476
  service: IService;