@serve.zone/interfaces 32.2.0 → 32.3.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/changelog.md +23 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/clusternode.d.ts +21 -0
- package/dist_ts/data/clusternode.js +345 -11
- package/dist_ts/platform/storage.d.ts +19 -0
- package/dist_ts/platform/storage.js +23 -3
- package/package.json +1 -1
- package/readme.md +35 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/clusternode.ts +457 -10
- package/ts/platform/storage.ts +27 -5
package/changelog.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-09-18 - 32.3.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Add `platform.normalizeStorageRequest(value)`, the contract's own statement of which storage
|
|
8
|
+
requests exist: it reads a body against the exact request schema and returns the request in its
|
|
9
|
+
contract shape — canonical environment keys, no empty `protection` block, no explicitly undefined
|
|
10
|
+
member, `retentionDays` within its bound, no `-0` or unsafe integer, plain JSON data members
|
|
11
|
+
only. `canonicalizeStorageRequest` and `createStorageRequestSha256` are now derived from it, so a
|
|
12
|
+
consumer that admits requests and the control plane that digests them apply one set of acceptance
|
|
13
|
+
rules instead of each keeping an aligned copy. The input is never mutated, the returned request
|
|
14
|
+
is fresh, and a body that is not a storage request is refused with `platform.StorageContractError`
|
|
15
|
+
whose message names the failing member.
|
|
16
|
+
- Add `data.validateSparkSwarmNodeHeartbeatRequest`, `data.validateSparkMetricsSampleRequest` and
|
|
17
|
+
`data.validateSparkActionResultRequest`, so all four Spark Swarm routes are judged by the
|
|
18
|
+
contract that owns their shapes instead of by member checks a consumer keeps beside them. Each
|
|
19
|
+
reads the exact key set, the sender's `protocol` offer and the reporter's identity, then names
|
|
20
|
+
every refused member of its payload — a heartbeat's metrics and runtime description down to each
|
|
21
|
+
described serve.zone service, a sample's observations and optional network rates, and an action
|
|
22
|
+
result whose status is one a node can report, never `pending`. They answer in the same
|
|
23
|
+
`Promise<string[]>` shape as `validateSparkSwarmObservationRequest`, refuse an accessor instead
|
|
24
|
+
of running it, and neither authenticate a node nor act on the body.
|
|
25
|
+
|
|
3
26
|
## 2026-09-18 - 32.2.0
|
|
4
27
|
|
|
5
28
|
### Features
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/interfaces',
|
|
6
|
-
version: '32.
|
|
6
|
+
version: '32.3.0',
|
|
7
7
|
description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLHVGQUF1RjtDQUNyRyxDQUFBIn0=
|
|
@@ -288,6 +288,27 @@ export declare const computeSwarmManagerSnapshotDigest: (swarmClusterIdArg: stri
|
|
|
288
288
|
export declare const validateSparkSwarmObservation: (observationArg: unknown) => Promise<string[]>;
|
|
289
289
|
export declare const validateSparkSwarmObservationRequest: (requestArg: unknown) => Promise<string[]>;
|
|
290
290
|
export declare const validateSparkSwarmObservationResponse: (responseArg: unknown, expectedRequestArg: ISparkSwarmObservationRequest) => Promise<string[]>;
|
|
291
|
+
/**
|
|
292
|
+
* Spark -> Cloudly body for `sparkSwarmNodeContracts.heartbeat.endpoint`.
|
|
293
|
+
*
|
|
294
|
+
* Stated here beside the shape it judges, so the server that reads the beat and the node that
|
|
295
|
+
* writes it apply one set of acceptance rules; a reader that kept its own member checks would
|
|
296
|
+
* decide on its own which beats exist.
|
|
297
|
+
*
|
|
298
|
+
* Every Spark route validator answers the same way — one string per refused member, empty for an
|
|
299
|
+
* admissible body, and the same `(body) => Promise<string[]>` signature, so a server can hold the
|
|
300
|
+
* four routes in one table.
|
|
301
|
+
*/
|
|
302
|
+
export declare const validateSparkSwarmNodeHeartbeatRequest: (requestArg: unknown) => Promise<string[]>;
|
|
303
|
+
/** Spark -> Cloudly body for `sparkSwarmNodeContracts.metricsSample.endpoint`. */
|
|
304
|
+
export declare const validateSparkMetricsSampleRequest: (requestArg: unknown) => Promise<string[]>;
|
|
305
|
+
/**
|
|
306
|
+
* Spark -> Cloudly body for `sparkSwarmNodeContracts.actionResult.endpoint`.
|
|
307
|
+
*
|
|
308
|
+
* `pending` is the one status a node never reports: an action it has not started has no result to
|
|
309
|
+
* post, and a server that accepted one would reopen an action it already handed out.
|
|
310
|
+
*/
|
|
311
|
+
export declare const validateSparkActionResultRequest: (requestArg: unknown) => Promise<string[]>;
|
|
291
312
|
export declare const createClusterRuntimeTargetSetDigestInput: (targetSetArg: IClusterRuntimeTargetSetReady) => string;
|
|
292
313
|
export declare const computeClusterRuntimeTargetSetDigest: (targetSetArg: IClusterRuntimeTargetSetReady) => Promise<TSha256Digest>;
|
|
293
314
|
export declare const validateClusterRuntimeTargetSet: (targetSetArg: unknown) => Promise<string[]>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
2
|
import { protocolRefusalHttpStatus, validateProtocolOfferOn } from '../protocol.js';
|
|
3
3
|
import { canonicalizeStrictJson, createCanonicalJsonSha256Hex, } from '../private/canonicaljson.js';
|
|
4
|
-
import { hasExactKeys } from '../private/dataproperties.js';
|
|
4
|
+
import { hasExactKeys, readDataElements, readDataProperties, } from '../private/dataproperties.js';
|
|
5
5
|
/**
|
|
6
6
|
* The Swarm-era Spark runtime's own routes.
|
|
7
7
|
*
|
|
@@ -286,6 +286,25 @@ export const validateSparkSwarmObservation = async (observationArg) => {
|
|
|
286
286
|
return ['Spark Swarm observation must be safely inspectable'];
|
|
287
287
|
}
|
|
288
288
|
};
|
|
289
|
+
/**
|
|
290
|
+
* Which node is reporting, in the two members every Spark route body carries.
|
|
291
|
+
*
|
|
292
|
+
* All four routes authenticate the same reporter from the same pair, so the rule lives here: a
|
|
293
|
+
* copy per route is how one route ends up admitting a bearer another refuses.
|
|
294
|
+
*/
|
|
295
|
+
const validateSparkReporterIdentity = (requestArg, labelArg) => {
|
|
296
|
+
const errors = [];
|
|
297
|
+
if (!isRuntimeIdentifier(requestArg.nodeId)) {
|
|
298
|
+
errors.push(`${labelArg} nodeId must be canonical`);
|
|
299
|
+
}
|
|
300
|
+
if (typeof requestArg.nodeToken !== 'string'
|
|
301
|
+
|| requestArg.nodeToken.length < 1
|
|
302
|
+
|| requestArg.nodeToken.length > 512
|
|
303
|
+
|| requestArg.nodeToken.trim() !== requestArg.nodeToken) {
|
|
304
|
+
errors.push(`${labelArg} nodeToken must be a bounded opaque token`);
|
|
305
|
+
}
|
|
306
|
+
return errors;
|
|
307
|
+
};
|
|
289
308
|
export const validateSparkSwarmObservationRequest = async (requestArg) => {
|
|
290
309
|
try {
|
|
291
310
|
if (!isRuntimeRecord(requestArg)
|
|
@@ -300,15 +319,7 @@ export const validateSparkSwarmObservationRequest = async (requestArg) => {
|
|
|
300
319
|
}
|
|
301
320
|
const errors = validateProtocolOfferOn(requestArg, 'Spark Swarm observation request protocol');
|
|
302
321
|
errors.push(...await validateSparkSwarmObservation(requestArg.observation));
|
|
303
|
-
|
|
304
|
-
errors.push('Spark Swarm observation request nodeId must be canonical');
|
|
305
|
-
}
|
|
306
|
-
if (typeof requestArg.nodeToken !== 'string'
|
|
307
|
-
|| requestArg.nodeToken.length < 1
|
|
308
|
-
|| requestArg.nodeToken.length > 512
|
|
309
|
-
|| requestArg.nodeToken.trim() !== requestArg.nodeToken) {
|
|
310
|
-
errors.push('Spark Swarm observation request nodeToken must be a bounded opaque token');
|
|
311
|
-
}
|
|
322
|
+
errors.push(...validateSparkReporterIdentity(requestArg, 'Spark Swarm observation request'));
|
|
312
323
|
return errors;
|
|
313
324
|
}
|
|
314
325
|
catch {
|
|
@@ -389,6 +400,329 @@ export const validateSparkSwarmObservationResponse = async (responseArg, expecte
|
|
|
389
400
|
return ['Spark Swarm observation response must be safely inspectable'];
|
|
390
401
|
}
|
|
391
402
|
};
|
|
403
|
+
/** A runtime description or a service name: bounded, and free of bytes JSON text cannot carry. */
|
|
404
|
+
const maximumSparkRuntimeTextLength = 512;
|
|
405
|
+
/** An operator-facing message, which may hold line breaks but is still bounded. */
|
|
406
|
+
const maximumSparkMessageTextLength = 2048;
|
|
407
|
+
/** `sparkSwarmNodeContracts.actionResult` states what a server keeps of an action's output. */
|
|
408
|
+
const maximumSparkActionResultTextLength = 4000;
|
|
409
|
+
/**
|
|
410
|
+
* The route's own byte bound refuses an oversized runtime description first; this stops an
|
|
411
|
+
* in-process caller from making the validator walk a list no route could have delivered.
|
|
412
|
+
*/
|
|
413
|
+
const maximumSparkRuntimeServices = 64;
|
|
414
|
+
const sparkCloudlyConnectionStatuses = new Set([
|
|
415
|
+
'not-configured',
|
|
416
|
+
'connecting',
|
|
417
|
+
'connected',
|
|
418
|
+
'failed',
|
|
419
|
+
]);
|
|
420
|
+
const sparkNodeModes = new Set(['cloudly', 'coreflow-node']);
|
|
421
|
+
const sparkActionResultStatuses = new Set([
|
|
422
|
+
'running',
|
|
423
|
+
'succeeded',
|
|
424
|
+
'failed',
|
|
425
|
+
]);
|
|
426
|
+
const isBoundedText = (valueArg, maximumLengthArg = maximumSparkRuntimeTextLength) => (typeof valueArg === 'string'
|
|
427
|
+
&& valueArg.length > 0
|
|
428
|
+
&& valueArg.length <= maximumLengthArg
|
|
429
|
+
&& !/[\u0000-\u001f\u007f]/u.test(valueArg));
|
|
430
|
+
/** Message text a node reports: line breaks and tabs are its content, other control bytes are not. */
|
|
431
|
+
const isBoundedMessageText = (valueArg, maximumLengthArg) => (typeof valueArg === 'string'
|
|
432
|
+
&& valueArg.length <= maximumLengthArg
|
|
433
|
+
&& !/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test(valueArg));
|
|
434
|
+
const isNonNegativeNumber = (valueArg, maximumArg = Number.MAX_SAFE_INTEGER) => (typeof valueArg === 'number'
|
|
435
|
+
&& Number.isFinite(valueArg)
|
|
436
|
+
&& !Object.is(valueArg, -0)
|
|
437
|
+
&& valueArg >= 0
|
|
438
|
+
&& valueArg <= maximumArg);
|
|
439
|
+
const isNonNegativeSafeInteger = (valueArg) => (Number.isSafeInteger(valueArg) && !Object.is(valueArg, -0) && valueArg >= 0);
|
|
440
|
+
/**
|
|
441
|
+
* One level of a Spark body: own enumerable data properties only, then the exact key set.
|
|
442
|
+
*
|
|
443
|
+
* The heartbeat, metrics-sample and action-result bodies are judged member by member rather than
|
|
444
|
+
* as canonical bytes, because only the observation is digested and those three legitimately carry
|
|
445
|
+
* fractional CPU, memory and rate observations that the digest's integer encoding would refuse.
|
|
446
|
+
* What the canonical reader also gave — a body that cannot run code or answer differently on a
|
|
447
|
+
* second read — is kept here by copying the data properties away from the caller's object before
|
|
448
|
+
* any member is judged.
|
|
449
|
+
*/
|
|
450
|
+
const readSparkRecord = (valueArg, labelArg, requiredKeysArg, optionalKeysArg = []) => {
|
|
451
|
+
const record = readDataProperties(valueArg);
|
|
452
|
+
if (record === null) {
|
|
453
|
+
return { record: null, errors: [`${labelArg} must contain plain JSON data properties`] };
|
|
454
|
+
}
|
|
455
|
+
if (!hasExactKeys(record, requiredKeysArg, optionalKeysArg)) {
|
|
456
|
+
return { record: null, errors: [`${labelArg} must use its exact schema`] };
|
|
457
|
+
}
|
|
458
|
+
return { record, errors: [] };
|
|
459
|
+
};
|
|
460
|
+
/**
|
|
461
|
+
* The envelope a Spark route body states before its payload: the exact key set, the reporter's
|
|
462
|
+
* protocol offer read by name, and the reporter's identity — in that order, because a server
|
|
463
|
+
* answers an unreadable offer before it looks at anything the body claims about a node.
|
|
464
|
+
*/
|
|
465
|
+
const readSparkRouteEnvelope = (requestArg, labelArg, requiredKeysArg, optionalKeysArg = []) => {
|
|
466
|
+
const reading = readSparkRecord(requestArg, labelArg, requiredKeysArg, optionalKeysArg);
|
|
467
|
+
if (reading.record === null) {
|
|
468
|
+
return reading;
|
|
469
|
+
}
|
|
470
|
+
return {
|
|
471
|
+
record: reading.record,
|
|
472
|
+
errors: [
|
|
473
|
+
...validateProtocolOfferOn(reading.record, `${labelArg} protocol`),
|
|
474
|
+
...validateSparkReporterIdentity(reading.record, labelArg),
|
|
475
|
+
],
|
|
476
|
+
};
|
|
477
|
+
};
|
|
478
|
+
/**
|
|
479
|
+
* One reason per member, collected rather than thrown.
|
|
480
|
+
*
|
|
481
|
+
* A Spark route answers a refusal before the node is authenticated, so the reporter only ever
|
|
482
|
+
* learns which member of its own body was wrong; naming that member is what lets an operator fix
|
|
483
|
+
* a node without reading the server's source.
|
|
484
|
+
*/
|
|
485
|
+
const createSparkFieldErrors = (recordArg, labelArg) => {
|
|
486
|
+
const errors = [];
|
|
487
|
+
return {
|
|
488
|
+
errors,
|
|
489
|
+
number: (keyArg, maximumArg) => {
|
|
490
|
+
if (!isNonNegativeNumber(recordArg[keyArg], maximumArg)) {
|
|
491
|
+
errors.push(`${labelArg} ${keyArg} must be a bounded non-negative number`);
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
percentage: (keyArg) => {
|
|
495
|
+
if (!isNonNegativeNumber(recordArg[keyArg], 100)) {
|
|
496
|
+
errors.push(`${labelArg} ${keyArg} must be a percentage between 0 and 100`);
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
count: (keyArg) => {
|
|
500
|
+
if (!isNonNegativeSafeInteger(recordArg[keyArg])) {
|
|
501
|
+
errors.push(`${labelArg} ${keyArg} must be a non-negative safe integer`);
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
instant: (keyArg) => {
|
|
505
|
+
if (!isPositiveSafeInteger(recordArg[keyArg])) {
|
|
506
|
+
errors.push(`${labelArg} ${keyArg} must be a positive safe integer timestamp`);
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
text: (keyArg) => {
|
|
510
|
+
if (!isBoundedText(recordArg[keyArg])) {
|
|
511
|
+
errors.push(`${labelArg} ${keyArg} must be bounded non-empty text`);
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
messageText: (keyArg, maximumLengthArg) => {
|
|
515
|
+
if (!isBoundedMessageText(recordArg[keyArg], maximumLengthArg)) {
|
|
516
|
+
errors.push(`${labelArg} ${keyArg} must be bounded message text`);
|
|
517
|
+
}
|
|
518
|
+
},
|
|
519
|
+
identifier: (keyArg) => {
|
|
520
|
+
if (!isRuntimeIdentifier(recordArg[keyArg])) {
|
|
521
|
+
errors.push(`${labelArg} ${keyArg} must be canonical`);
|
|
522
|
+
}
|
|
523
|
+
},
|
|
524
|
+
boolean: (keyArg) => {
|
|
525
|
+
if (typeof recordArg[keyArg] !== 'boolean') {
|
|
526
|
+
errors.push(`${labelArg} ${keyArg} must be a boolean`);
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
member: (keyArg, allowedArg) => {
|
|
530
|
+
if (!allowedArg.has(recordArg[keyArg])) {
|
|
531
|
+
errors.push(`${labelArg} ${keyArg} is outside the supported values`);
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
/** Runs `checkArg` only when the optional member is present at all. */
|
|
535
|
+
optional: (keyArg, checkArg) => {
|
|
536
|
+
if (recordArg[keyArg] !== undefined) {
|
|
537
|
+
checkArg(keyArg);
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
};
|
|
541
|
+
};
|
|
542
|
+
const validateSparkNodeMetrics = (valueArg, labelArg) => {
|
|
543
|
+
const reading = readSparkRecord(valueArg, labelArg, [
|
|
544
|
+
'cpuUsagePercent',
|
|
545
|
+
'memoryUsedMB',
|
|
546
|
+
'memoryAvailableMB',
|
|
547
|
+
'diskUsedGB',
|
|
548
|
+
'diskAvailableGB',
|
|
549
|
+
'containerCount',
|
|
550
|
+
'timestamp',
|
|
551
|
+
]);
|
|
552
|
+
if (reading.record === null) {
|
|
553
|
+
return reading.errors;
|
|
554
|
+
}
|
|
555
|
+
const field = createSparkFieldErrors(reading.record, labelArg);
|
|
556
|
+
field.percentage('cpuUsagePercent');
|
|
557
|
+
field.number('memoryUsedMB');
|
|
558
|
+
field.number('memoryAvailableMB');
|
|
559
|
+
field.number('diskUsedGB');
|
|
560
|
+
field.number('diskAvailableGB');
|
|
561
|
+
field.count('containerCount');
|
|
562
|
+
field.instant('timestamp');
|
|
563
|
+
return field.errors;
|
|
564
|
+
};
|
|
565
|
+
const validateNodeMetricsSample = (valueArg, labelArg) => {
|
|
566
|
+
const reading = readSparkRecord(valueArg, labelArg, ['timestamp', 'cpuUsagePercent', 'memoryUsedMB', 'memoryAvailableMB'], ['networkRxBytesPerSec', 'networkTxBytesPerSec']);
|
|
567
|
+
if (reading.record === null) {
|
|
568
|
+
return reading.errors;
|
|
569
|
+
}
|
|
570
|
+
const field = createSparkFieldErrors(reading.record, labelArg);
|
|
571
|
+
field.instant('timestamp');
|
|
572
|
+
field.percentage('cpuUsagePercent');
|
|
573
|
+
field.number('memoryUsedMB');
|
|
574
|
+
field.number('memoryAvailableMB');
|
|
575
|
+
field.optional('networkRxBytesPerSec', field.number);
|
|
576
|
+
field.optional('networkTxBytesPerSec', field.number);
|
|
577
|
+
return field.errors;
|
|
578
|
+
};
|
|
579
|
+
const validateServeZoneServiceRuntimeInfo = (valueArg, labelArg) => {
|
|
580
|
+
const reading = readSparkRecord(valueArg, labelArg, ['name', 'running', 'checkedAt'], [
|
|
581
|
+
'serviceId',
|
|
582
|
+
'image',
|
|
583
|
+
'desiredImage',
|
|
584
|
+
'imageVersion',
|
|
585
|
+
'runningImageId',
|
|
586
|
+
'runningTaskCount',
|
|
587
|
+
'updatedAt',
|
|
588
|
+
'error',
|
|
589
|
+
]);
|
|
590
|
+
if (reading.record === null) {
|
|
591
|
+
return reading.errors;
|
|
592
|
+
}
|
|
593
|
+
const field = createSparkFieldErrors(reading.record, labelArg);
|
|
594
|
+
field.text('name');
|
|
595
|
+
field.boolean('running');
|
|
596
|
+
field.instant('checkedAt');
|
|
597
|
+
field.optional('serviceId', field.text);
|
|
598
|
+
field.optional('image', field.text);
|
|
599
|
+
field.optional('desiredImage', field.text);
|
|
600
|
+
field.optional('imageVersion', field.text);
|
|
601
|
+
field.optional('runningImageId', field.text);
|
|
602
|
+
field.optional('runningTaskCount', field.count);
|
|
603
|
+
field.optional('updatedAt', field.instant);
|
|
604
|
+
field.optional('error', (keyArg) => field.messageText(keyArg, maximumSparkMessageTextLength));
|
|
605
|
+
return field.errors;
|
|
606
|
+
};
|
|
607
|
+
const validateSparkNodeRuntimeInfo = (valueArg, labelArg) => {
|
|
608
|
+
const reading = readSparkRecord(valueArg, labelArg, [
|
|
609
|
+
'runtime',
|
|
610
|
+
'nodeId',
|
|
611
|
+
'platform',
|
|
612
|
+
'arch',
|
|
613
|
+
'sparkVersion',
|
|
614
|
+
'cloudlyConnectionStatus',
|
|
615
|
+
'dockerAvailable',
|
|
616
|
+
'checkedAt',
|
|
617
|
+
], [
|
|
618
|
+
'mode',
|
|
619
|
+
'hostname',
|
|
620
|
+
'osRelease',
|
|
621
|
+
'cloudlyUrl',
|
|
622
|
+
'swarmNodeId',
|
|
623
|
+
'serveZoneServices',
|
|
624
|
+
'lastError',
|
|
625
|
+
]);
|
|
626
|
+
if (reading.record === null) {
|
|
627
|
+
return reading.errors;
|
|
628
|
+
}
|
|
629
|
+
const runtimeInfo = reading.record;
|
|
630
|
+
const field = createSparkFieldErrors(runtimeInfo, labelArg);
|
|
631
|
+
if (runtimeInfo.runtime !== 'spark') {
|
|
632
|
+
field.errors.push(`${labelArg} runtime must be spark`);
|
|
633
|
+
}
|
|
634
|
+
field.identifier('nodeId');
|
|
635
|
+
field.text('platform');
|
|
636
|
+
field.text('arch');
|
|
637
|
+
field.text('sparkVersion');
|
|
638
|
+
field.member('cloudlyConnectionStatus', sparkCloudlyConnectionStatuses);
|
|
639
|
+
field.boolean('dockerAvailable');
|
|
640
|
+
field.instant('checkedAt');
|
|
641
|
+
field.optional('mode', (keyArg) => field.member(keyArg, sparkNodeModes));
|
|
642
|
+
field.optional('hostname', field.text);
|
|
643
|
+
field.optional('osRelease', field.text);
|
|
644
|
+
field.optional('cloudlyUrl', field.text);
|
|
645
|
+
field.optional('swarmNodeId', field.identifier);
|
|
646
|
+
field.optional('lastError', (keyArg) => field.messageText(keyArg, maximumSparkMessageTextLength));
|
|
647
|
+
if (runtimeInfo.serveZoneServices !== undefined) {
|
|
648
|
+
const services = readDataElements(runtimeInfo.serveZoneServices, maximumSparkRuntimeServices);
|
|
649
|
+
if (services === null) {
|
|
650
|
+
field.errors.push(`${labelArg} serveZoneServices must be a bounded list of at most `
|
|
651
|
+
+ `${maximumSparkRuntimeServices} described services`);
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
services.forEach((serviceArg, indexArg) => {
|
|
655
|
+
field.errors.push(...validateServeZoneServiceRuntimeInfo(serviceArg, `${labelArg} serveZoneServices[${indexArg}]`));
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
return field.errors;
|
|
660
|
+
};
|
|
661
|
+
/**
|
|
662
|
+
* Spark -> Cloudly body for `sparkSwarmNodeContracts.heartbeat.endpoint`.
|
|
663
|
+
*
|
|
664
|
+
* Stated here beside the shape it judges, so the server that reads the beat and the node that
|
|
665
|
+
* writes it apply one set of acceptance rules; a reader that kept its own member checks would
|
|
666
|
+
* decide on its own which beats exist.
|
|
667
|
+
*
|
|
668
|
+
* Every Spark route validator answers the same way — one string per refused member, empty for an
|
|
669
|
+
* admissible body, and the same `(body) => Promise<string[]>` signature, so a server can hold the
|
|
670
|
+
* four routes in one table.
|
|
671
|
+
*/
|
|
672
|
+
export const validateSparkSwarmNodeHeartbeatRequest = async (requestArg) => {
|
|
673
|
+
const label = 'Spark Swarm node heartbeat request';
|
|
674
|
+
try {
|
|
675
|
+
const envelope = readSparkRouteEnvelope(requestArg, label, ['nodeId', 'nodeToken', 'protocol', 'metrics', 'runtimeInfo']);
|
|
676
|
+
if (envelope.record === null) {
|
|
677
|
+
return envelope.errors;
|
|
678
|
+
}
|
|
679
|
+
envelope.errors.push(...validateSparkNodeMetrics(envelope.record.metrics, `${label} metrics`));
|
|
680
|
+
envelope.errors.push(...validateSparkNodeRuntimeInfo(envelope.record.runtimeInfo, `${label} runtimeInfo`));
|
|
681
|
+
return envelope.errors;
|
|
682
|
+
}
|
|
683
|
+
catch {
|
|
684
|
+
return [`${label} must be safely inspectable`];
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
/** Spark -> Cloudly body for `sparkSwarmNodeContracts.metricsSample.endpoint`. */
|
|
688
|
+
export const validateSparkMetricsSampleRequest = async (requestArg) => {
|
|
689
|
+
const label = 'Spark metrics sample request';
|
|
690
|
+
try {
|
|
691
|
+
const envelope = readSparkRouteEnvelope(requestArg, label, ['nodeId', 'nodeToken', 'protocol', 'sample']);
|
|
692
|
+
if (envelope.record === null) {
|
|
693
|
+
return envelope.errors;
|
|
694
|
+
}
|
|
695
|
+
envelope.errors.push(...validateNodeMetricsSample(envelope.record.sample, `${label} sample`));
|
|
696
|
+
return envelope.errors;
|
|
697
|
+
}
|
|
698
|
+
catch {
|
|
699
|
+
return [`${label} must be safely inspectable`];
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
/**
|
|
703
|
+
* Spark -> Cloudly body for `sparkSwarmNodeContracts.actionResult.endpoint`.
|
|
704
|
+
*
|
|
705
|
+
* `pending` is the one status a node never reports: an action it has not started has no result to
|
|
706
|
+
* post, and a server that accepted one would reopen an action it already handed out.
|
|
707
|
+
*/
|
|
708
|
+
export const validateSparkActionResultRequest = async (requestArg) => {
|
|
709
|
+
const label = 'Spark action result request';
|
|
710
|
+
try {
|
|
711
|
+
const envelope = readSparkRouteEnvelope(requestArg, label, ['nodeId', 'nodeToken', 'protocol', 'actionId', 'status'], ['resultText']);
|
|
712
|
+
if (envelope.record === null) {
|
|
713
|
+
return envelope.errors;
|
|
714
|
+
}
|
|
715
|
+
const field = createSparkFieldErrors(envelope.record, label);
|
|
716
|
+
field.identifier('actionId');
|
|
717
|
+
field.member('status', sparkActionResultStatuses);
|
|
718
|
+
field.optional('resultText', (keyArg) => field.messageText(keyArg, maximumSparkActionResultTextLength));
|
|
719
|
+
envelope.errors.push(...field.errors);
|
|
720
|
+
return envelope.errors;
|
|
721
|
+
}
|
|
722
|
+
catch {
|
|
723
|
+
return [`${label} must be safely inspectable`];
|
|
724
|
+
}
|
|
725
|
+
};
|
|
392
726
|
/**
|
|
393
727
|
* The exact five members of a target that the set digest is taken over.
|
|
394
728
|
*
|
|
@@ -571,4 +905,4 @@ export const validateClusterNodeCorestore = (valueArg, pathArg = 'cluster node c
|
|
|
571
905
|
}
|
|
572
906
|
return errors;
|
|
573
907
|
};
|
|
574
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2x1c3Rlcm5vZGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kYXRhL2NsdXN0ZXJub2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBR3pDLE9BQU8sRUFBRSx5QkFBeUIsRUFBRSx1QkFBdUIsRUFBdUIsTUFBTSxnQkFBZ0IsQ0FBQztBQUN6RyxPQUFPLEVBQ0wsc0JBQXNCLEVBQ3RCLDRCQUE0QixHQUM3QixNQUFNLDZCQUE2QixDQUFDO0FBQ3JDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQWlFNUQ7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFDSCxNQUFNLENBQUMsTUFBTSx1QkFBdUIsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ25ELCtGQUErRjtJQUMvRixTQUFTLEVBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQztRQUN2QixRQUFRLEVBQUUsOEJBQThCO1FBQ3hDLGVBQWUsRUFBRSxNQUFNO1FBQ3ZCLGdCQUFnQixFQUFFLElBQUk7S0FDZCxDQUFDO0lBQ1gsNEVBQTRFO0lBQzVFLGFBQWEsRUFBRSxNQUFNLENBQUMsTUFBTSxDQUFDO1FBQzNCLFFBQVEsRUFBRSxtQ0FBbUM7UUFDN0MsZUFBZSxFQUFFLElBQUk7UUFDckIsZ0JBQWdCLEVBQUUsTUFBTTtLQUNoQixDQUFDO0lBQ1gsdUZBQXVGO0lBQ3ZGLFlBQVksRUFBRSxNQUFNLENBQUMsTUFBTSxDQUFDO1FBQzFCLFFBQVEsRUFBRSxrQ0FBa0M7UUFDNUMsZUFBZSxFQUFFLElBQUk7UUFDckIsZ0JBQWdCLEVBQUUsSUFBSTtLQUNkLENBQUM7SUFDWDs7OztPQUlHO0lBQ0gsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQztRQUM5QixRQUFRLEVBQUUsc0NBQXNDO1FBQ2hELGVBQWUsRUFBRSxPQUFPO1FBQ3hCLGdCQUFnQixFQUFFLElBQUk7S0FDZCxDQUFDO0lB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
|
|
908
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2x1c3Rlcm5vZGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kYXRhL2NsdXN0ZXJub2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBR3pDLE9BQU8sRUFBRSx5QkFBeUIsRUFBRSx1QkFBdUIsRUFBdUIsTUFBTSxnQkFBZ0IsQ0FBQztBQUN6RyxPQUFPLEVBQ0wsc0JBQXNCLEVBQ3RCLDRCQUE0QixHQUM3QixNQUFNLDZCQUE2QixDQUFDO0FBQ3JDLE9BQU8sRUFDTCxZQUFZLEVBQ1osZ0JBQWdCLEVBQ2hCLGtCQUFrQixHQUNuQixNQUFNLDhCQUE4QixDQUFDO0FBaUV0Qzs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILE1BQU0sQ0FBQyxNQUFNLHVCQUF1QixHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDbkQsK0ZBQStGO0lBQy9GLFNBQVMsRUFBRSxNQUFNLENBQUMsTUFBTSxDQUFDO1FBQ3ZCLFFBQVEsRUFBRSw4QkFBOEI7UUFDeEMsZUFBZSxFQUFFLE1BQU07UUFDdkIsZ0JBQWdCLEVBQUUsSUFBSTtLQUNkLENBQUM7SUFDWCw0RUFBNEU7SUFDNUUsYUFBYSxFQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUM7UUFDM0IsUUFBUSxFQUFFLG1DQUFtQztRQUM3QyxlQUFlLEVBQUUsSUFBSTtRQUNyQixnQkFBZ0IsRUFBRSxNQUFNO0tBQ2hCLENBQUM7SUFDWCx1RkFBdUY7SUFDdkYsWUFBWSxFQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUM7UUFDMUIsUUFBUSxFQUFFLGtDQUFrQztRQUM1QyxlQUFlLEVBQUUsSUFBSTtRQUNyQixnQkFBZ0IsRUFBRSxJQUFJO0tBQ2QsQ0FBQztJQUNYOzs7O09BSUc7SUFDSCxnQkFBZ0IsRUFBRSxNQUFNLENBQUMsTUFBTSxDQUFDO1FBQzlCLFFBQVEsRUFBRSxzQ0FBc0M7UUFDaEQsZUFBZSxFQUFFLE9BQU87UUFDeEIsZ0JB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
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TAppStoreStorageRequest } from '../appstore/types.js';
|
|
1
2
|
import type { TSecretManagementScope } from '../data/secret.js';
|
|
2
3
|
/**
|
|
3
4
|
* Backend-neutral storage vocabulary shared by App Store manifests, control
|
|
@@ -187,6 +188,24 @@ export type TResolvedStorageBinding = IResolvedFilesystemStorageBinding | IResol
|
|
|
187
188
|
export declare class StorageContractError extends Error {
|
|
188
189
|
constructor(reasonArg: string);
|
|
189
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* The one judgement of what a storage request is: the request read against its exact schema and
|
|
193
|
+
* returned in its contract shape — required members present, optional members carried only when
|
|
194
|
+
* the request declares them, and every value within the bounds an adapter can fulfil.
|
|
195
|
+
*
|
|
196
|
+
* A control plane that admits requests, a fulfillment adapter that provisions them, and an App
|
|
197
|
+
* Store front end that assembles them all have to agree on the same acceptance rules; a second
|
|
198
|
+
* copy of them anywhere is a second answer to which requests exist, and the digest one side issues
|
|
199
|
+
* would name a request the other never accepted. `canonicalizeStorageRequest` and
|
|
200
|
+
* `createStorageRequestSha256` are derived from this function rather than the other way round, so
|
|
201
|
+
* a consumer that needs the judged request itself does not have to re-parse its own canonical
|
|
202
|
+
* bytes.
|
|
203
|
+
*
|
|
204
|
+
* The returned request is a fresh value; the input is never read again after normalization and
|
|
205
|
+
* never mutated. Anything that is not a storage request is refused with `StorageContractError`,
|
|
206
|
+
* whose message names the failing field so a consumer can map it to its own status vocabulary.
|
|
207
|
+
*/
|
|
208
|
+
export declare const normalizeStorageRequest: (valueArg: unknown) => TAppStoreStorageRequest;
|
|
190
209
|
/**
|
|
191
210
|
* The exact bytes a storage request's digest is taken over: the request normalized to its contract
|
|
192
211
|
* shape, written as strict canonical JSON — keys in UTF-16 code-unit order, no whitespace, no
|
|
@@ -16,7 +16,27 @@ export class StorageContractError extends Error {
|
|
|
16
16
|
const fail = (reasonArg) => {
|
|
17
17
|
throw new StorageContractError(reasonArg);
|
|
18
18
|
};
|
|
19
|
-
const { normalizeStorageRequest } = createStorageContractReaders(fail);
|
|
19
|
+
const { normalizeStorageRequest: readStorageRequest } = createStorageContractReaders(fail);
|
|
20
|
+
/**
|
|
21
|
+
* The one judgement of what a storage request is: the request read against its exact schema and
|
|
22
|
+
* returned in its contract shape — required members present, optional members carried only when
|
|
23
|
+
* the request declares them, and every value within the bounds an adapter can fulfil.
|
|
24
|
+
*
|
|
25
|
+
* A control plane that admits requests, a fulfillment adapter that provisions them, and an App
|
|
26
|
+
* Store front end that assembles them all have to agree on the same acceptance rules; a second
|
|
27
|
+
* copy of them anywhere is a second answer to which requests exist, and the digest one side issues
|
|
28
|
+
* would name a request the other never accepted. `canonicalizeStorageRequest` and
|
|
29
|
+
* `createStorageRequestSha256` are derived from this function rather than the other way round, so
|
|
30
|
+
* a consumer that needs the judged request itself does not have to re-parse its own canonical
|
|
31
|
+
* bytes.
|
|
32
|
+
*
|
|
33
|
+
* The returned request is a fresh value; the input is never read again after normalization and
|
|
34
|
+
* never mutated. Anything that is not a storage request is refused with `StorageContractError`,
|
|
35
|
+
* whose message names the failing field so a consumer can map it to its own status vocabulary.
|
|
36
|
+
*/
|
|
37
|
+
export const normalizeStorageRequest = (valueArg) => {
|
|
38
|
+
return readStorageRequest(valueArg, 'storage request');
|
|
39
|
+
};
|
|
20
40
|
/**
|
|
21
41
|
* The exact bytes a storage request's digest is taken over: the request normalized to its contract
|
|
22
42
|
* shape, written as strict canonical JSON — keys in UTF-16 code-unit order, no whitespace, no
|
|
@@ -26,7 +46,7 @@ const { normalizeStorageRequest } = createStorageContractReaders(fail);
|
|
|
26
46
|
* unjudged body would name a request no adapter could fulfil.
|
|
27
47
|
*/
|
|
28
48
|
export const canonicalizeStorageRequest = (valueArg) => {
|
|
29
|
-
return canonicalizeStrictJson(normalizeStorageRequest(valueArg
|
|
49
|
+
return canonicalizeStrictJson(normalizeStorageRequest(valueArg), fail);
|
|
30
50
|
};
|
|
31
51
|
/**
|
|
32
52
|
* The storage request digest: bare lowercase hex SHA-256 over the canonical request, with no
|
|
@@ -41,4 +61,4 @@ export const createStorageRequestSha256 = async (valueArg) => {
|
|
|
41
61
|
return createCanonicalJsonSha256Hex(canonicalizeStorageRequest(valueArg), fail);
|
|
42
62
|
};
|
|
43
63
|
export { storageRequestCanonicalDigestGoldenVectors, } from './storage.golden.js';
|
|
44
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
64
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvcmFnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3BsYXRmb3JtL3N0b3JhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsT0FBTyxFQUNMLHNCQUFzQixFQUN0Qiw0QkFBNEIsR0FDN0IsTUFBTSw2QkFBNkIsQ0FBQztBQUNyQyxPQUFPLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQVE3RTs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSxnQ0FBZ0MsR0FBRztJQUM5QyxhQUFhLEVBQUUsZUFBZTtDQUN0QixDQUFDO0FBaU9YLE1BQU0sT0FBTyxvQkFBcUIsU0FBUSxLQUFLO0lBQzdDLFlBQW1CLFNBQWlCO1FBQ2xDLEtBQUssQ0FBQywyQkFBMkIsU0FBUyxFQUFFLENBQUMsQ0FBQztRQUM5QyxJQUFJLENBQUMsSUFBSSxHQUFHLHNCQUFzQixDQUFDO0lBQ3JDLENBQUM7Q0FDRjtBQUVELE1BQU0sSUFBSSxHQUFHLENBQUMsU0FBaUIsRUFBUyxFQUFFO0lBQ3hDLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUM1QyxDQUFDLENBQUM7QUFFRixNQUFNLEVBQUUsdUJBQXVCLEVBQUUsa0JBQWtCLEVBQUUsR0FDbkQsNEJBQTRCLENBQUMsSUFBSSxDQUFDLENBQUM7QUFFckM7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFDSCxNQUFNLENBQUMsTUFBTSx1QkFBdUIsR0FBRyxDQUNyQyxRQUFpQixFQUNRLEVBQUU7SUFDM0IsT0FBTyxrQkFBa0IsQ0FBQyxRQUFRLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztBQUN6RCxDQUFDLENBQUM7QUFFRjs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sMEJBQTBCLEdBQUcsQ0FBQyxRQUFpQixFQUFVLEVBQUU7SUFDdEUsT0FBTyxzQkFBc0IsQ0FBQyx1QkFBdUIsQ0FBQyxRQUFRLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUN6RSxDQUFDLENBQUM7QUFFRjs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sQ0FBQyxNQUFNLDBCQUEwQixHQUFHLEtBQUssRUFDN0MsUUFBaUIsRUFDQSxFQUFFO0lBQ25CLE9BQU8sNEJBQTRCLENBQUMsMEJBQTBCLENBQUMsUUFBUSxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFDbEYsQ0FBQyxDQUFDO0FBRUYsT0FBTyxFQUNMLDBDQUEwQyxHQUUzQyxNQUFNLHFCQUFxQixDQUFDIn0=
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -146,6 +146,22 @@ rather than with this release. `ISparkSwarmNodeHeartbeatRequest` is now owned he
|
|
|
146
146
|
so Cloudly and Spark no longer carry private copies of it, and every Swarm-era body
|
|
147
147
|
— request and answer alike — carries `protocol` and is judged in the same order.
|
|
148
148
|
|
|
149
|
+
Each of the four routes has its validator here, so a server never writes its own
|
|
150
|
+
member checks: `data.validateSparkSwarmNodeHeartbeatRequest`,
|
|
151
|
+
`data.validateSparkMetricsSampleRequest`, `data.validateSparkActionResultRequest`
|
|
152
|
+
and `data.validateSparkSwarmObservationRequest`. All four answer the same way —
|
|
153
|
+
one string per refused member, naming the member, and an empty list for an
|
|
154
|
+
admissible body — so a route table can hold them side by side. They read the
|
|
155
|
+
envelope first (exact key set, the sender's offer, then the reporter's identity),
|
|
156
|
+
then the payload: a heartbeat's metrics and full runtime description including
|
|
157
|
+
each described serve.zone service, a sample's four observations and optional
|
|
158
|
+
network rates, and an action result whose status is one a node can actually
|
|
159
|
+
report, never `pending`. Bodies are judged member by member rather than as
|
|
160
|
+
canonical bytes, because only the observation is digested and the others carry
|
|
161
|
+
fractional CPU, memory and rate observations; every level is copied away from the
|
|
162
|
+
caller's object first, so an accessor is refused instead of run. None of them
|
|
163
|
+
authenticates a node or acts on the body.
|
|
164
|
+
|
|
149
165
|
Cloudly must authenticate the bearer against current Spark authority and fence
|
|
150
166
|
credential generation and session epoch transactionally before recording host
|
|
151
167
|
liveness. `ISparkNodeHeartbeatReceipt` contains only server-derived acceptance
|
|
@@ -1308,6 +1324,25 @@ around the request. A body that is not a storage request is refused with
|
|
|
1308
1324
|
taken over. `platform.storageRequestCanonicalDigestGoldenVectors` states both
|
|
1309
1325
|
for a filesystem request and for both object-storage deliveries.
|
|
1310
1326
|
|
|
1327
|
+
Which requests exist at all is stated by the same contract:
|
|
1328
|
+
|
|
1329
|
+
```typescript
|
|
1330
|
+
const storageRequest = platform.normalizeStorageRequest(declaredRequest);
|
|
1331
|
+
```
|
|
1332
|
+
|
|
1333
|
+
`platform.normalizeStorageRequest` reads a body against the exact request schema
|
|
1334
|
+
and returns the request in its contract shape — required members present,
|
|
1335
|
+
optional members carried only when declared, canonical environment keys, no
|
|
1336
|
+
empty `protection` block, no explicitly undefined member, no `-0` or unsafe
|
|
1337
|
+
integer, and plain JSON data members only. It is the value the other two
|
|
1338
|
+
functions are derived from: canonicalizing and digesting a request normalize it
|
|
1339
|
+
first, so a consumer that admits a request through this function and a control
|
|
1340
|
+
plane that digests it cannot accept different sets of requests. The input is
|
|
1341
|
+
never mutated and the returned request is a fresh object. A body that is not a
|
|
1342
|
+
storage request is refused with `platform.StorageContractError`, whose message
|
|
1343
|
+
names the failing member so a consumer can map the refusal to its own status
|
|
1344
|
+
vocabulary instead of restating the rules.
|
|
1345
|
+
|
|
1311
1346
|
Portable manifests and resolved bindings intentionally have no fields for
|
|
1312
1347
|
Synology, NFS, Kerberos, Corestore, Kubernetes, Docker drivers, servers,
|
|
1313
1348
|
exports, mount options, provider credential values, or local fallback paths. Runtimes
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/data/clusternode.ts
CHANGED
|
@@ -6,7 +6,11 @@ import {
|
|
|
6
6
|
canonicalizeStrictJson,
|
|
7
7
|
createCanonicalJsonSha256Hex,
|
|
8
8
|
} from '../private/canonicaljson.js';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
hasExactKeys,
|
|
11
|
+
readDataElements,
|
|
12
|
+
readDataProperties,
|
|
13
|
+
} from '../private/dataproperties.js';
|
|
10
14
|
|
|
11
15
|
export interface IClusterNodeMetrics {
|
|
12
16
|
cpuUsagePercent: number;
|
|
@@ -610,6 +614,29 @@ export const validateSparkSwarmObservation = async (
|
|
|
610
614
|
}
|
|
611
615
|
};
|
|
612
616
|
|
|
617
|
+
/**
|
|
618
|
+
* Which node is reporting, in the two members every Spark route body carries.
|
|
619
|
+
*
|
|
620
|
+
* All four routes authenticate the same reporter from the same pair, so the rule lives here: a
|
|
621
|
+
* copy per route is how one route ends up admitting a bearer another refuses.
|
|
622
|
+
*/
|
|
623
|
+
const validateSparkReporterIdentity = (
|
|
624
|
+
requestArg: Record<string, unknown>,
|
|
625
|
+
labelArg: string,
|
|
626
|
+
): string[] => {
|
|
627
|
+
const errors: string[] = [];
|
|
628
|
+
if (!isRuntimeIdentifier(requestArg.nodeId)) {
|
|
629
|
+
errors.push(`${labelArg} nodeId must be canonical`);
|
|
630
|
+
}
|
|
631
|
+
if (typeof requestArg.nodeToken !== 'string'
|
|
632
|
+
|| requestArg.nodeToken.length < 1
|
|
633
|
+
|| requestArg.nodeToken.length > 512
|
|
634
|
+
|| requestArg.nodeToken.trim() !== requestArg.nodeToken) {
|
|
635
|
+
errors.push(`${labelArg} nodeToken must be a bounded opaque token`);
|
|
636
|
+
}
|
|
637
|
+
return errors;
|
|
638
|
+
};
|
|
639
|
+
|
|
613
640
|
export const validateSparkSwarmObservationRequest = async (
|
|
614
641
|
requestArg: unknown,
|
|
615
642
|
): Promise<string[]> => {
|
|
@@ -632,15 +659,10 @@ export const validateSparkSwarmObservationRequest = async (
|
|
|
632
659
|
'Spark Swarm observation request protocol',
|
|
633
660
|
);
|
|
634
661
|
errors.push(...await validateSparkSwarmObservation(requestArg.observation));
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|| requestArg.nodeToken.length < 1
|
|
640
|
-
|| requestArg.nodeToken.length > 512
|
|
641
|
-
|| requestArg.nodeToken.trim() !== requestArg.nodeToken) {
|
|
642
|
-
errors.push('Spark Swarm observation request nodeToken must be a bounded opaque token');
|
|
643
|
-
}
|
|
662
|
+
errors.push(...validateSparkReporterIdentity(
|
|
663
|
+
requestArg,
|
|
664
|
+
'Spark Swarm observation request',
|
|
665
|
+
));
|
|
644
666
|
return errors;
|
|
645
667
|
} catch {
|
|
646
668
|
return ['Spark Swarm observation request must be safely inspectable'];
|
|
@@ -735,6 +757,431 @@ export const validateSparkSwarmObservationResponse = async (
|
|
|
735
757
|
}
|
|
736
758
|
};
|
|
737
759
|
|
|
760
|
+
/** A runtime description or a service name: bounded, and free of bytes JSON text cannot carry. */
|
|
761
|
+
const maximumSparkRuntimeTextLength = 512;
|
|
762
|
+
/** An operator-facing message, which may hold line breaks but is still bounded. */
|
|
763
|
+
const maximumSparkMessageTextLength = 2048;
|
|
764
|
+
/** `sparkSwarmNodeContracts.actionResult` states what a server keeps of an action's output. */
|
|
765
|
+
const maximumSparkActionResultTextLength = 4000;
|
|
766
|
+
/**
|
|
767
|
+
* The route's own byte bound refuses an oversized runtime description first; this stops an
|
|
768
|
+
* in-process caller from making the validator walk a list no route could have delivered.
|
|
769
|
+
*/
|
|
770
|
+
const maximumSparkRuntimeServices = 64;
|
|
771
|
+
|
|
772
|
+
const sparkCloudlyConnectionStatuses = new Set<TSparkCloudlyConnectionStatus>([
|
|
773
|
+
'not-configured',
|
|
774
|
+
'connecting',
|
|
775
|
+
'connected',
|
|
776
|
+
'failed',
|
|
777
|
+
]);
|
|
778
|
+
const sparkNodeModes = new Set<TSparkNodeMode>(['cloudly', 'coreflow-node']);
|
|
779
|
+
const sparkActionResultStatuses = new Set<TNodeActionStatus>([
|
|
780
|
+
'running',
|
|
781
|
+
'succeeded',
|
|
782
|
+
'failed',
|
|
783
|
+
]);
|
|
784
|
+
|
|
785
|
+
const isBoundedText = (
|
|
786
|
+
valueArg: unknown,
|
|
787
|
+
maximumLengthArg = maximumSparkRuntimeTextLength,
|
|
788
|
+
): valueArg is string => (
|
|
789
|
+
typeof valueArg === 'string'
|
|
790
|
+
&& valueArg.length > 0
|
|
791
|
+
&& valueArg.length <= maximumLengthArg
|
|
792
|
+
&& !/[\u0000-\u001f\u007f]/u.test(valueArg)
|
|
793
|
+
);
|
|
794
|
+
|
|
795
|
+
/** Message text a node reports: line breaks and tabs are its content, other control bytes are not. */
|
|
796
|
+
const isBoundedMessageText = (
|
|
797
|
+
valueArg: unknown,
|
|
798
|
+
maximumLengthArg: number,
|
|
799
|
+
): valueArg is string => (
|
|
800
|
+
typeof valueArg === 'string'
|
|
801
|
+
&& valueArg.length <= maximumLengthArg
|
|
802
|
+
&& !/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test(valueArg)
|
|
803
|
+
);
|
|
804
|
+
|
|
805
|
+
const isNonNegativeNumber = (
|
|
806
|
+
valueArg: unknown,
|
|
807
|
+
maximumArg = Number.MAX_SAFE_INTEGER,
|
|
808
|
+
): valueArg is number => (
|
|
809
|
+
typeof valueArg === 'number'
|
|
810
|
+
&& Number.isFinite(valueArg)
|
|
811
|
+
&& !Object.is(valueArg, -0)
|
|
812
|
+
&& valueArg >= 0
|
|
813
|
+
&& valueArg <= maximumArg
|
|
814
|
+
);
|
|
815
|
+
|
|
816
|
+
const isNonNegativeSafeInteger = (valueArg: unknown): valueArg is number => (
|
|
817
|
+
Number.isSafeInteger(valueArg) && !Object.is(valueArg, -0) && (valueArg as number) >= 0
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
/** A record a Spark body states, detached from the caller's object, or `null` with the reason. */
|
|
821
|
+
interface ISparkRecordReading {
|
|
822
|
+
record: Record<string, unknown> | null;
|
|
823
|
+
errors: string[];
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* One level of a Spark body: own enumerable data properties only, then the exact key set.
|
|
828
|
+
*
|
|
829
|
+
* The heartbeat, metrics-sample and action-result bodies are judged member by member rather than
|
|
830
|
+
* as canonical bytes, because only the observation is digested and those three legitimately carry
|
|
831
|
+
* fractional CPU, memory and rate observations that the digest's integer encoding would refuse.
|
|
832
|
+
* What the canonical reader also gave — a body that cannot run code or answer differently on a
|
|
833
|
+
* second read — is kept here by copying the data properties away from the caller's object before
|
|
834
|
+
* any member is judged.
|
|
835
|
+
*/
|
|
836
|
+
const readSparkRecord = (
|
|
837
|
+
valueArg: unknown,
|
|
838
|
+
labelArg: string,
|
|
839
|
+
requiredKeysArg: readonly string[],
|
|
840
|
+
optionalKeysArg: readonly string[] = [],
|
|
841
|
+
): ISparkRecordReading => {
|
|
842
|
+
const record = readDataProperties(valueArg);
|
|
843
|
+
if (record === null) {
|
|
844
|
+
return { record: null, errors: [`${labelArg} must contain plain JSON data properties`] };
|
|
845
|
+
}
|
|
846
|
+
if (!hasExactKeys(record, requiredKeysArg, optionalKeysArg)) {
|
|
847
|
+
return { record: null, errors: [`${labelArg} must use its exact schema`] };
|
|
848
|
+
}
|
|
849
|
+
return { record, errors: [] };
|
|
850
|
+
};
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* The envelope a Spark route body states before its payload: the exact key set, the reporter's
|
|
854
|
+
* protocol offer read by name, and the reporter's identity — in that order, because a server
|
|
855
|
+
* answers an unreadable offer before it looks at anything the body claims about a node.
|
|
856
|
+
*/
|
|
857
|
+
const readSparkRouteEnvelope = (
|
|
858
|
+
requestArg: unknown,
|
|
859
|
+
labelArg: string,
|
|
860
|
+
requiredKeysArg: readonly string[],
|
|
861
|
+
optionalKeysArg: readonly string[] = [],
|
|
862
|
+
): ISparkRecordReading => {
|
|
863
|
+
const reading = readSparkRecord(requestArg, labelArg, requiredKeysArg, optionalKeysArg);
|
|
864
|
+
if (reading.record === null) {
|
|
865
|
+
return reading;
|
|
866
|
+
}
|
|
867
|
+
return {
|
|
868
|
+
record: reading.record,
|
|
869
|
+
errors: [
|
|
870
|
+
...validateProtocolOfferOn(reading.record, `${labelArg} protocol`),
|
|
871
|
+
...validateSparkReporterIdentity(reading.record, labelArg),
|
|
872
|
+
],
|
|
873
|
+
};
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* One reason per member, collected rather than thrown.
|
|
878
|
+
*
|
|
879
|
+
* A Spark route answers a refusal before the node is authenticated, so the reporter only ever
|
|
880
|
+
* learns which member of its own body was wrong; naming that member is what lets an operator fix
|
|
881
|
+
* a node without reading the server's source.
|
|
882
|
+
*/
|
|
883
|
+
const createSparkFieldErrors = (
|
|
884
|
+
recordArg: Record<string, unknown>,
|
|
885
|
+
labelArg: string,
|
|
886
|
+
) => {
|
|
887
|
+
const errors: string[] = [];
|
|
888
|
+
return {
|
|
889
|
+
errors,
|
|
890
|
+
number: (keyArg: string, maximumArg?: number): void => {
|
|
891
|
+
if (!isNonNegativeNumber(recordArg[keyArg], maximumArg)) {
|
|
892
|
+
errors.push(`${labelArg} ${keyArg} must be a bounded non-negative number`);
|
|
893
|
+
}
|
|
894
|
+
},
|
|
895
|
+
percentage: (keyArg: string): void => {
|
|
896
|
+
if (!isNonNegativeNumber(recordArg[keyArg], 100)) {
|
|
897
|
+
errors.push(`${labelArg} ${keyArg} must be a percentage between 0 and 100`);
|
|
898
|
+
}
|
|
899
|
+
},
|
|
900
|
+
count: (keyArg: string): void => {
|
|
901
|
+
if (!isNonNegativeSafeInteger(recordArg[keyArg])) {
|
|
902
|
+
errors.push(`${labelArg} ${keyArg} must be a non-negative safe integer`);
|
|
903
|
+
}
|
|
904
|
+
},
|
|
905
|
+
instant: (keyArg: string): void => {
|
|
906
|
+
if (!isPositiveSafeInteger(recordArg[keyArg])) {
|
|
907
|
+
errors.push(`${labelArg} ${keyArg} must be a positive safe integer timestamp`);
|
|
908
|
+
}
|
|
909
|
+
},
|
|
910
|
+
text: (keyArg: string): void => {
|
|
911
|
+
if (!isBoundedText(recordArg[keyArg])) {
|
|
912
|
+
errors.push(`${labelArg} ${keyArg} must be bounded non-empty text`);
|
|
913
|
+
}
|
|
914
|
+
},
|
|
915
|
+
messageText: (keyArg: string, maximumLengthArg: number): void => {
|
|
916
|
+
if (!isBoundedMessageText(recordArg[keyArg], maximumLengthArg)) {
|
|
917
|
+
errors.push(`${labelArg} ${keyArg} must be bounded message text`);
|
|
918
|
+
}
|
|
919
|
+
},
|
|
920
|
+
identifier: (keyArg: string): void => {
|
|
921
|
+
if (!isRuntimeIdentifier(recordArg[keyArg])) {
|
|
922
|
+
errors.push(`${labelArg} ${keyArg} must be canonical`);
|
|
923
|
+
}
|
|
924
|
+
},
|
|
925
|
+
boolean: (keyArg: string): void => {
|
|
926
|
+
if (typeof recordArg[keyArg] !== 'boolean') {
|
|
927
|
+
errors.push(`${labelArg} ${keyArg} must be a boolean`);
|
|
928
|
+
}
|
|
929
|
+
},
|
|
930
|
+
member: <TMember extends string>(keyArg: string, allowedArg: ReadonlySet<TMember>): void => {
|
|
931
|
+
if (!allowedArg.has(recordArg[keyArg] as TMember)) {
|
|
932
|
+
errors.push(`${labelArg} ${keyArg} is outside the supported values`);
|
|
933
|
+
}
|
|
934
|
+
},
|
|
935
|
+
/** Runs `checkArg` only when the optional member is present at all. */
|
|
936
|
+
optional: (keyArg: string, checkArg: (keyArg: string) => void): void => {
|
|
937
|
+
if (recordArg[keyArg] !== undefined) {
|
|
938
|
+
checkArg(keyArg);
|
|
939
|
+
}
|
|
940
|
+
},
|
|
941
|
+
};
|
|
942
|
+
};
|
|
943
|
+
|
|
944
|
+
const validateSparkNodeMetrics = (valueArg: unknown, labelArg: string): string[] => {
|
|
945
|
+
const reading = readSparkRecord(valueArg, labelArg, [
|
|
946
|
+
'cpuUsagePercent',
|
|
947
|
+
'memoryUsedMB',
|
|
948
|
+
'memoryAvailableMB',
|
|
949
|
+
'diskUsedGB',
|
|
950
|
+
'diskAvailableGB',
|
|
951
|
+
'containerCount',
|
|
952
|
+
'timestamp',
|
|
953
|
+
]);
|
|
954
|
+
if (reading.record === null) {
|
|
955
|
+
return reading.errors;
|
|
956
|
+
}
|
|
957
|
+
const field = createSparkFieldErrors(reading.record, labelArg);
|
|
958
|
+
field.percentage('cpuUsagePercent');
|
|
959
|
+
field.number('memoryUsedMB');
|
|
960
|
+
field.number('memoryAvailableMB');
|
|
961
|
+
field.number('diskUsedGB');
|
|
962
|
+
field.number('diskAvailableGB');
|
|
963
|
+
field.count('containerCount');
|
|
964
|
+
field.instant('timestamp');
|
|
965
|
+
return field.errors;
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
const validateNodeMetricsSample = (valueArg: unknown, labelArg: string): string[] => {
|
|
969
|
+
const reading = readSparkRecord(
|
|
970
|
+
valueArg,
|
|
971
|
+
labelArg,
|
|
972
|
+
['timestamp', 'cpuUsagePercent', 'memoryUsedMB', 'memoryAvailableMB'],
|
|
973
|
+
['networkRxBytesPerSec', 'networkTxBytesPerSec'],
|
|
974
|
+
);
|
|
975
|
+
if (reading.record === null) {
|
|
976
|
+
return reading.errors;
|
|
977
|
+
}
|
|
978
|
+
const field = createSparkFieldErrors(reading.record, labelArg);
|
|
979
|
+
field.instant('timestamp');
|
|
980
|
+
field.percentage('cpuUsagePercent');
|
|
981
|
+
field.number('memoryUsedMB');
|
|
982
|
+
field.number('memoryAvailableMB');
|
|
983
|
+
field.optional('networkRxBytesPerSec', field.number);
|
|
984
|
+
field.optional('networkTxBytesPerSec', field.number);
|
|
985
|
+
return field.errors;
|
|
986
|
+
};
|
|
987
|
+
|
|
988
|
+
const validateServeZoneServiceRuntimeInfo = (
|
|
989
|
+
valueArg: unknown,
|
|
990
|
+
labelArg: string,
|
|
991
|
+
): string[] => {
|
|
992
|
+
const reading = readSparkRecord(
|
|
993
|
+
valueArg,
|
|
994
|
+
labelArg,
|
|
995
|
+
['name', 'running', 'checkedAt'],
|
|
996
|
+
[
|
|
997
|
+
'serviceId',
|
|
998
|
+
'image',
|
|
999
|
+
'desiredImage',
|
|
1000
|
+
'imageVersion',
|
|
1001
|
+
'runningImageId',
|
|
1002
|
+
'runningTaskCount',
|
|
1003
|
+
'updatedAt',
|
|
1004
|
+
'error',
|
|
1005
|
+
],
|
|
1006
|
+
);
|
|
1007
|
+
if (reading.record === null) {
|
|
1008
|
+
return reading.errors;
|
|
1009
|
+
}
|
|
1010
|
+
const field = createSparkFieldErrors(reading.record, labelArg);
|
|
1011
|
+
field.text('name');
|
|
1012
|
+
field.boolean('running');
|
|
1013
|
+
field.instant('checkedAt');
|
|
1014
|
+
field.optional('serviceId', field.text);
|
|
1015
|
+
field.optional('image', field.text);
|
|
1016
|
+
field.optional('desiredImage', field.text);
|
|
1017
|
+
field.optional('imageVersion', field.text);
|
|
1018
|
+
field.optional('runningImageId', field.text);
|
|
1019
|
+
field.optional('runningTaskCount', field.count);
|
|
1020
|
+
field.optional('updatedAt', field.instant);
|
|
1021
|
+
field.optional('error', (keyArg) => field.messageText(keyArg, maximumSparkMessageTextLength));
|
|
1022
|
+
return field.errors;
|
|
1023
|
+
};
|
|
1024
|
+
|
|
1025
|
+
const validateSparkNodeRuntimeInfo = (valueArg: unknown, labelArg: string): string[] => {
|
|
1026
|
+
const reading = readSparkRecord(
|
|
1027
|
+
valueArg,
|
|
1028
|
+
labelArg,
|
|
1029
|
+
[
|
|
1030
|
+
'runtime',
|
|
1031
|
+
'nodeId',
|
|
1032
|
+
'platform',
|
|
1033
|
+
'arch',
|
|
1034
|
+
'sparkVersion',
|
|
1035
|
+
'cloudlyConnectionStatus',
|
|
1036
|
+
'dockerAvailable',
|
|
1037
|
+
'checkedAt',
|
|
1038
|
+
],
|
|
1039
|
+
[
|
|
1040
|
+
'mode',
|
|
1041
|
+
'hostname',
|
|
1042
|
+
'osRelease',
|
|
1043
|
+
'cloudlyUrl',
|
|
1044
|
+
'swarmNodeId',
|
|
1045
|
+
'serveZoneServices',
|
|
1046
|
+
'lastError',
|
|
1047
|
+
],
|
|
1048
|
+
);
|
|
1049
|
+
if (reading.record === null) {
|
|
1050
|
+
return reading.errors;
|
|
1051
|
+
}
|
|
1052
|
+
const runtimeInfo = reading.record;
|
|
1053
|
+
const field = createSparkFieldErrors(runtimeInfo, labelArg);
|
|
1054
|
+
if (runtimeInfo.runtime !== 'spark') {
|
|
1055
|
+
field.errors.push(`${labelArg} runtime must be spark`);
|
|
1056
|
+
}
|
|
1057
|
+
field.identifier('nodeId');
|
|
1058
|
+
field.text('platform');
|
|
1059
|
+
field.text('arch');
|
|
1060
|
+
field.text('sparkVersion');
|
|
1061
|
+
field.member('cloudlyConnectionStatus', sparkCloudlyConnectionStatuses);
|
|
1062
|
+
field.boolean('dockerAvailable');
|
|
1063
|
+
field.instant('checkedAt');
|
|
1064
|
+
field.optional('mode', (keyArg) => field.member(keyArg, sparkNodeModes));
|
|
1065
|
+
field.optional('hostname', field.text);
|
|
1066
|
+
field.optional('osRelease', field.text);
|
|
1067
|
+
field.optional('cloudlyUrl', field.text);
|
|
1068
|
+
field.optional('swarmNodeId', field.identifier);
|
|
1069
|
+
field.optional('lastError', (keyArg) => field.messageText(keyArg, maximumSparkMessageTextLength));
|
|
1070
|
+
if (runtimeInfo.serveZoneServices !== undefined) {
|
|
1071
|
+
const services = readDataElements(runtimeInfo.serveZoneServices, maximumSparkRuntimeServices);
|
|
1072
|
+
if (services === null) {
|
|
1073
|
+
field.errors.push(
|
|
1074
|
+
`${labelArg} serveZoneServices must be a bounded list of at most `
|
|
1075
|
+
+ `${maximumSparkRuntimeServices} described services`,
|
|
1076
|
+
);
|
|
1077
|
+
} else {
|
|
1078
|
+
services.forEach((serviceArg, indexArg) => {
|
|
1079
|
+
field.errors.push(...validateServeZoneServiceRuntimeInfo(
|
|
1080
|
+
serviceArg,
|
|
1081
|
+
`${labelArg} serveZoneServices[${indexArg}]`,
|
|
1082
|
+
));
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
return field.errors;
|
|
1087
|
+
};
|
|
1088
|
+
|
|
1089
|
+
/**
|
|
1090
|
+
* Spark -> Cloudly body for `sparkSwarmNodeContracts.heartbeat.endpoint`.
|
|
1091
|
+
*
|
|
1092
|
+
* Stated here beside the shape it judges, so the server that reads the beat and the node that
|
|
1093
|
+
* writes it apply one set of acceptance rules; a reader that kept its own member checks would
|
|
1094
|
+
* decide on its own which beats exist.
|
|
1095
|
+
*
|
|
1096
|
+
* Every Spark route validator answers the same way — one string per refused member, empty for an
|
|
1097
|
+
* admissible body, and the same `(body) => Promise<string[]>` signature, so a server can hold the
|
|
1098
|
+
* four routes in one table.
|
|
1099
|
+
*/
|
|
1100
|
+
export const validateSparkSwarmNodeHeartbeatRequest = async (
|
|
1101
|
+
requestArg: unknown,
|
|
1102
|
+
): Promise<string[]> => {
|
|
1103
|
+
const label = 'Spark Swarm node heartbeat request';
|
|
1104
|
+
try {
|
|
1105
|
+
const envelope = readSparkRouteEnvelope(
|
|
1106
|
+
requestArg,
|
|
1107
|
+
label,
|
|
1108
|
+
['nodeId', 'nodeToken', 'protocol', 'metrics', 'runtimeInfo'],
|
|
1109
|
+
);
|
|
1110
|
+
if (envelope.record === null) {
|
|
1111
|
+
return envelope.errors;
|
|
1112
|
+
}
|
|
1113
|
+
envelope.errors.push(...validateSparkNodeMetrics(
|
|
1114
|
+
envelope.record.metrics,
|
|
1115
|
+
`${label} metrics`,
|
|
1116
|
+
));
|
|
1117
|
+
envelope.errors.push(...validateSparkNodeRuntimeInfo(
|
|
1118
|
+
envelope.record.runtimeInfo,
|
|
1119
|
+
`${label} runtimeInfo`,
|
|
1120
|
+
));
|
|
1121
|
+
return envelope.errors;
|
|
1122
|
+
} catch {
|
|
1123
|
+
return [`${label} must be safely inspectable`];
|
|
1124
|
+
}
|
|
1125
|
+
};
|
|
1126
|
+
|
|
1127
|
+
/** Spark -> Cloudly body for `sparkSwarmNodeContracts.metricsSample.endpoint`. */
|
|
1128
|
+
export const validateSparkMetricsSampleRequest = async (
|
|
1129
|
+
requestArg: unknown,
|
|
1130
|
+
): Promise<string[]> => {
|
|
1131
|
+
const label = 'Spark metrics sample request';
|
|
1132
|
+
try {
|
|
1133
|
+
const envelope = readSparkRouteEnvelope(
|
|
1134
|
+
requestArg,
|
|
1135
|
+
label,
|
|
1136
|
+
['nodeId', 'nodeToken', 'protocol', 'sample'],
|
|
1137
|
+
);
|
|
1138
|
+
if (envelope.record === null) {
|
|
1139
|
+
return envelope.errors;
|
|
1140
|
+
}
|
|
1141
|
+
envelope.errors.push(...validateNodeMetricsSample(
|
|
1142
|
+
envelope.record.sample,
|
|
1143
|
+
`${label} sample`,
|
|
1144
|
+
));
|
|
1145
|
+
return envelope.errors;
|
|
1146
|
+
} catch {
|
|
1147
|
+
return [`${label} must be safely inspectable`];
|
|
1148
|
+
}
|
|
1149
|
+
};
|
|
1150
|
+
|
|
1151
|
+
/**
|
|
1152
|
+
* Spark -> Cloudly body for `sparkSwarmNodeContracts.actionResult.endpoint`.
|
|
1153
|
+
*
|
|
1154
|
+
* `pending` is the one status a node never reports: an action it has not started has no result to
|
|
1155
|
+
* post, and a server that accepted one would reopen an action it already handed out.
|
|
1156
|
+
*/
|
|
1157
|
+
export const validateSparkActionResultRequest = async (
|
|
1158
|
+
requestArg: unknown,
|
|
1159
|
+
): Promise<string[]> => {
|
|
1160
|
+
const label = 'Spark action result request';
|
|
1161
|
+
try {
|
|
1162
|
+
const envelope = readSparkRouteEnvelope(
|
|
1163
|
+
requestArg,
|
|
1164
|
+
label,
|
|
1165
|
+
['nodeId', 'nodeToken', 'protocol', 'actionId', 'status'],
|
|
1166
|
+
['resultText'],
|
|
1167
|
+
);
|
|
1168
|
+
if (envelope.record === null) {
|
|
1169
|
+
return envelope.errors;
|
|
1170
|
+
}
|
|
1171
|
+
const field = createSparkFieldErrors(envelope.record, label);
|
|
1172
|
+
field.identifier('actionId');
|
|
1173
|
+
field.member('status', sparkActionResultStatuses);
|
|
1174
|
+
field.optional('resultText', (keyArg) => field.messageText(
|
|
1175
|
+
keyArg,
|
|
1176
|
+
maximumSparkActionResultTextLength,
|
|
1177
|
+
));
|
|
1178
|
+
envelope.errors.push(...field.errors);
|
|
1179
|
+
return envelope.errors;
|
|
1180
|
+
} catch {
|
|
1181
|
+
return [`${label} must be safely inspectable`];
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1184
|
+
|
|
738
1185
|
/**
|
|
739
1186
|
* The exact five members of a target that the set digest is taken over.
|
|
740
1187
|
*
|
package/ts/platform/storage.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TAppStoreStorageRequest } from '../appstore/types.js';
|
|
1
2
|
import type { TSecretManagementScope } from '../data/secret.js';
|
|
2
3
|
import {
|
|
3
4
|
canonicalizeStrictJson,
|
|
@@ -253,7 +254,31 @@ const fail = (reasonArg: string): never => {
|
|
|
253
254
|
throw new StorageContractError(reasonArg);
|
|
254
255
|
};
|
|
255
256
|
|
|
256
|
-
const { normalizeStorageRequest } =
|
|
257
|
+
const { normalizeStorageRequest: readStorageRequest } =
|
|
258
|
+
createStorageContractReaders(fail);
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* The one judgement of what a storage request is: the request read against its exact schema and
|
|
262
|
+
* returned in its contract shape — required members present, optional members carried only when
|
|
263
|
+
* the request declares them, and every value within the bounds an adapter can fulfil.
|
|
264
|
+
*
|
|
265
|
+
* A control plane that admits requests, a fulfillment adapter that provisions them, and an App
|
|
266
|
+
* Store front end that assembles them all have to agree on the same acceptance rules; a second
|
|
267
|
+
* copy of them anywhere is a second answer to which requests exist, and the digest one side issues
|
|
268
|
+
* would name a request the other never accepted. `canonicalizeStorageRequest` and
|
|
269
|
+
* `createStorageRequestSha256` are derived from this function rather than the other way round, so
|
|
270
|
+
* a consumer that needs the judged request itself does not have to re-parse its own canonical
|
|
271
|
+
* bytes.
|
|
272
|
+
*
|
|
273
|
+
* The returned request is a fresh value; the input is never read again after normalization and
|
|
274
|
+
* never mutated. Anything that is not a storage request is refused with `StorageContractError`,
|
|
275
|
+
* whose message names the failing field so a consumer can map it to its own status vocabulary.
|
|
276
|
+
*/
|
|
277
|
+
export const normalizeStorageRequest = (
|
|
278
|
+
valueArg: unknown,
|
|
279
|
+
): TAppStoreStorageRequest => {
|
|
280
|
+
return readStorageRequest(valueArg, 'storage request');
|
|
281
|
+
};
|
|
257
282
|
|
|
258
283
|
/**
|
|
259
284
|
* The exact bytes a storage request's digest is taken over: the request normalized to its contract
|
|
@@ -264,10 +289,7 @@ const { normalizeStorageRequest } = createStorageContractReaders(fail);
|
|
|
264
289
|
* unjudged body would name a request no adapter could fulfil.
|
|
265
290
|
*/
|
|
266
291
|
export const canonicalizeStorageRequest = (valueArg: unknown): string => {
|
|
267
|
-
return canonicalizeStrictJson(
|
|
268
|
-
normalizeStorageRequest(valueArg, 'storage request'),
|
|
269
|
-
fail,
|
|
270
|
-
);
|
|
292
|
+
return canonicalizeStrictJson(normalizeStorageRequest(valueArg), fail);
|
|
271
293
|
};
|
|
272
294
|
|
|
273
295
|
/**
|