@serve.zone/interfaces 32.0.2 → 32.2.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 +37 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/index.d.ts +1 -0
- package/dist_ts/data/index.js +2 -1
- package/dist_ts/data/noderetirement.d.ts +39 -0
- package/dist_ts/data/noderetirement.js +31 -0
- package/dist_ts/data/serviceruntime.d.ts +10 -2
- package/dist_ts/data/serviceruntime.js +4 -3
- package/dist_ts/platform/storage.d.ts +29 -2
- package/dist_ts/platform/storage.golden.d.ts +31 -0
- package/dist_ts/platform/storage.golden.js +84 -0
- package/dist_ts/platform/storage.js +37 -1
- package/dist_ts/platform/storagemigration.js +11 -287
- package/dist_ts/private/storagecontract.d.ts +40 -0
- package/dist_ts/private/storagecontract.js +399 -0
- package/dist_ts/requests/node.d.ts +25 -0
- package/package.json +1 -1
- package/readme.md +40 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/index.ts +1 -0
- package/ts/data/noderetirement.ts +63 -0
- package/ts/data/serviceruntime.ts +12 -3
- package/ts/platform/storage.golden.ts +97 -0
- package/ts/platform/storage.ts +59 -2
- package/ts/platform/storagemigration.ts +31 -440
- package/ts/private/storagecontract.ts +623 -0
- package/ts/requests/node.ts +29 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The readers a storage control body is judged by, and the one normalizer that turns a storage
|
|
3
|
+
* request into the exact value its digest is taken over.
|
|
4
|
+
*
|
|
5
|
+
* Two modules judge the same bytes in their own error vocabulary — `platform/storage.ts` for a
|
|
6
|
+
* storage request on its own and `platform/storagemigration.ts` for the migration control plane
|
|
7
|
+
* that carries one — so the rules take the failure they report through. A second copy of them
|
|
8
|
+
* would be a second answer to what a request is, and a request digest that a control plane issues
|
|
9
|
+
* and a consumer re-derives would stop being one value.
|
|
10
|
+
*/
|
|
11
|
+
import type { IAppStoreFilesystemStorageRequest, IAppStoreObjectStorageRequest, TAppStoreStorageRequest } from '../appstore/types.js';
|
|
12
|
+
import type { TObjectStorageDelivery } from '../platform/storage.js';
|
|
13
|
+
import type { TCanonicalJsonFailure } from './canonicaljson.js';
|
|
14
|
+
/** Byte bounds every reader of a storage control string applies. */
|
|
15
|
+
export declare const storageContractStringLimits: Readonly<{
|
|
16
|
+
maximumConfiguredStringBytes: 2048;
|
|
17
|
+
maximumEnvironmentKeyBytes: 256;
|
|
18
|
+
maximumSecretPathBytes: 2048;
|
|
19
|
+
}>;
|
|
20
|
+
export declare const createStorageContractReaders: (failArg: TCanonicalJsonFailure) => {
|
|
21
|
+
utf8ByteLength: (valueArg: string) => number;
|
|
22
|
+
readRecord: (valueArg: unknown, fieldNameArg: string) => Record<string, unknown>;
|
|
23
|
+
readDenseArray: (valueArg: unknown, fieldNameArg: string, minimumLengthArg: number, maximumLengthArg: number) => unknown[];
|
|
24
|
+
assertKeys: (recordArg: Record<string, unknown>, requiredKeysArg: readonly string[], optionalKeysArg: readonly string[], fieldNameArg: string) => void;
|
|
25
|
+
assertExactKeys: (recordArg: Record<string, unknown>, expectedKeysArg: readonly string[], fieldNameArg: string) => void;
|
|
26
|
+
readString: (valueArg: unknown, fieldNameArg: string, maximumBytesArg?: number) => string;
|
|
27
|
+
readIdentifier: (valueArg: unknown, fieldNameArg: string) => string;
|
|
28
|
+
readSha256: (valueArg: unknown, fieldNameArg: string) => string;
|
|
29
|
+
readSafeInteger: (valueArg: unknown, fieldNameArg: string, minimumArg: number, maximumArg?: number) => number;
|
|
30
|
+
readBoolean: (valueArg: unknown, fieldNameArg: string) => boolean;
|
|
31
|
+
readLiteral: <TLiteral extends string>(valueArg: unknown, allowedArg: readonly TLiteral[], fieldNameArg: string) => TLiteral;
|
|
32
|
+
readCapacityQuantity: (valueArg: unknown, fieldNameArg: string) => string;
|
|
33
|
+
capacityQuantityBytes: (valueArg: string) => bigint;
|
|
34
|
+
readEnvironmentKey: (valueArg: unknown, fieldNameArg: string) => string;
|
|
35
|
+
normalizeCapacityRequest: (valueArg: unknown, fieldNameArg: string) => NonNullable<TAppStoreStorageRequest["capacity"]>;
|
|
36
|
+
normalizeDelivery: (valueArg: unknown, fieldNameArg: string) => TObjectStorageDelivery;
|
|
37
|
+
normalizeObjectStorageRequest: (valueArg: unknown, fieldNameArg: string) => IAppStoreObjectStorageRequest;
|
|
38
|
+
normalizeFilesystemStorageRequest: (valueArg: unknown, fieldNameArg: string) => IAppStoreFilesystemStorageRequest;
|
|
39
|
+
normalizeStorageRequest: (valueArg: unknown, fieldNameArg: string) => TAppStoreStorageRequest;
|
|
40
|
+
};
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import { normalizeServiceAbsolutePath } from '../data/service.js';
|
|
2
|
+
/** Byte bounds every reader of a storage control string applies. */
|
|
3
|
+
export const storageContractStringLimits = Object.freeze({
|
|
4
|
+
maximumConfiguredStringBytes: 2048,
|
|
5
|
+
maximumEnvironmentKeyBytes: 256,
|
|
6
|
+
maximumSecretPathBytes: 2048,
|
|
7
|
+
});
|
|
8
|
+
const canonicalIdPattern = /^[A-Za-z0-9][A-Za-z0-9._:@-]{0,191}$/;
|
|
9
|
+
const environmentKeyPattern = /^[A-Z_][A-Z0-9_]{0,255}$/;
|
|
10
|
+
const sha256Pattern = /^[a-f0-9]{64}$/;
|
|
11
|
+
const capacityPattern = /^[1-9][0-9]*(?:KiB|MiB|GiB|TiB)$/;
|
|
12
|
+
/**
|
|
13
|
+
* The mount a Secret file is delivered under. A filesystem request that mounts it, or anything
|
|
14
|
+
* containing it, would shadow every credential the platform writes for the same service.
|
|
15
|
+
*/
|
|
16
|
+
const secretDeliveryRoot = '/run/secrets';
|
|
17
|
+
export const createStorageContractReaders = (failArg) => {
|
|
18
|
+
const utf8ByteLength = (valueArg) => {
|
|
19
|
+
return new TextEncoder().encode(valueArg).byteLength;
|
|
20
|
+
};
|
|
21
|
+
const readRecord = (valueArg, fieldNameArg) => {
|
|
22
|
+
if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg)) {
|
|
23
|
+
return failArg(`${fieldNameArg} must be a plain object`);
|
|
24
|
+
}
|
|
25
|
+
const prototype = Object.getPrototypeOf(valueArg);
|
|
26
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
27
|
+
return failArg(`${fieldNameArg} must be a plain object`);
|
|
28
|
+
}
|
|
29
|
+
const record = valueArg;
|
|
30
|
+
const ownKeys = Reflect.ownKeys(record);
|
|
31
|
+
if (ownKeys.some((keyArg) => typeof keyArg !== 'string')) {
|
|
32
|
+
return failArg(`${fieldNameArg} must not contain symbol keys`);
|
|
33
|
+
}
|
|
34
|
+
for (const key of ownKeys) {
|
|
35
|
+
const descriptor = Object.getOwnPropertyDescriptor(record, key);
|
|
36
|
+
if (!descriptor ||
|
|
37
|
+
!descriptor.enumerable ||
|
|
38
|
+
!Object.hasOwn(descriptor, 'value') ||
|
|
39
|
+
descriptor.value === undefined) {
|
|
40
|
+
return failArg(`${fieldNameArg} must contain enumerable JSON data properties only`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return record;
|
|
44
|
+
};
|
|
45
|
+
const readDenseArray = (valueArg, fieldNameArg, minimumLengthArg, maximumLengthArg) => {
|
|
46
|
+
if (!Array.isArray(valueArg) ||
|
|
47
|
+
Object.getPrototypeOf(valueArg) !== Array.prototype) {
|
|
48
|
+
return failArg(`${fieldNameArg} must be a dense plain array`);
|
|
49
|
+
}
|
|
50
|
+
if (valueArg.length < minimumLengthArg ||
|
|
51
|
+
valueArg.length > maximumLengthArg) {
|
|
52
|
+
return failArg(`${fieldNameArg} must contain between ${minimumLengthArg} and ${maximumLengthArg} entries`);
|
|
53
|
+
}
|
|
54
|
+
for (let index = 0; index < valueArg.length; index++) {
|
|
55
|
+
if (!Object.hasOwn(valueArg, index)) {
|
|
56
|
+
return failArg(`${fieldNameArg} must not contain sparse entries`);
|
|
57
|
+
}
|
|
58
|
+
const descriptor = Object.getOwnPropertyDescriptor(valueArg, String(index));
|
|
59
|
+
if (!descriptor ||
|
|
60
|
+
!descriptor.enumerable ||
|
|
61
|
+
!Object.hasOwn(descriptor, 'value') ||
|
|
62
|
+
descriptor.value === undefined) {
|
|
63
|
+
return failArg(`${fieldNameArg} must contain enumerable JSON data entries only`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const key of Reflect.ownKeys(valueArg)) {
|
|
67
|
+
if (key === 'length')
|
|
68
|
+
continue;
|
|
69
|
+
if (typeof key !== 'string' ||
|
|
70
|
+
!/^(?:0|[1-9][0-9]*)$/.test(key) ||
|
|
71
|
+
Number(key) >= valueArg.length) {
|
|
72
|
+
return failArg(`${fieldNameArg} must not contain extra properties`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return valueArg;
|
|
76
|
+
};
|
|
77
|
+
const assertKeys = (recordArg, requiredKeysArg, optionalKeysArg, fieldNameArg) => {
|
|
78
|
+
const actualKeys = Object.keys(recordArg).sort();
|
|
79
|
+
const allowedKeys = new Set([...requiredKeysArg, ...optionalKeysArg]);
|
|
80
|
+
if (requiredKeysArg.some((keyArg) => !Object.hasOwn(recordArg, keyArg)) ||
|
|
81
|
+
actualKeys.some((keyArg) => !allowedKeys.has(keyArg))) {
|
|
82
|
+
failArg(`${fieldNameArg} does not match its exact schema`);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const assertExactKeys = (recordArg, expectedKeysArg, fieldNameArg) => {
|
|
86
|
+
assertKeys(recordArg, expectedKeysArg, [], fieldNameArg);
|
|
87
|
+
};
|
|
88
|
+
const readString = (valueArg, fieldNameArg, maximumBytesArg = storageContractStringLimits.maximumConfiguredStringBytes) => {
|
|
89
|
+
if (typeof valueArg !== 'string' ||
|
|
90
|
+
valueArg.length === 0 ||
|
|
91
|
+
utf8ByteLength(valueArg) > maximumBytesArg ||
|
|
92
|
+
/[\u0000-\u001f\u007f]/u.test(valueArg)) {
|
|
93
|
+
return failArg(`${fieldNameArg} must be a bounded non-empty JSON string`);
|
|
94
|
+
}
|
|
95
|
+
for (let index = 0; index < valueArg.length; index++) {
|
|
96
|
+
const codeUnit = valueArg.charCodeAt(index);
|
|
97
|
+
if (codeUnit >= 0xd800 && codeUnit <= 0xdbff) {
|
|
98
|
+
const following = valueArg.charCodeAt(index + 1);
|
|
99
|
+
if (following < 0xdc00 || following > 0xdfff) {
|
|
100
|
+
return failArg(`${fieldNameArg} contains an unpaired UTF-16 surrogate`);
|
|
101
|
+
}
|
|
102
|
+
index++;
|
|
103
|
+
}
|
|
104
|
+
else if (codeUnit >= 0xdc00 && codeUnit <= 0xdfff) {
|
|
105
|
+
return failArg(`${fieldNameArg} contains an unpaired UTF-16 surrogate`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return valueArg;
|
|
109
|
+
};
|
|
110
|
+
const readIdentifier = (valueArg, fieldNameArg) => {
|
|
111
|
+
if (typeof valueArg !== 'string' || !canonicalIdPattern.test(valueArg)) {
|
|
112
|
+
return failArg(`${fieldNameArg} must be a canonical identifier`);
|
|
113
|
+
}
|
|
114
|
+
return valueArg;
|
|
115
|
+
};
|
|
116
|
+
const readSha256 = (valueArg, fieldNameArg) => {
|
|
117
|
+
if (typeof valueArg !== 'string' || !sha256Pattern.test(valueArg)) {
|
|
118
|
+
return failArg(`${fieldNameArg} must be a bare lowercase 64-hex SHA-256 digest`);
|
|
119
|
+
}
|
|
120
|
+
return valueArg;
|
|
121
|
+
};
|
|
122
|
+
const readSafeInteger = (valueArg, fieldNameArg, minimumArg, maximumArg = Number.MAX_SAFE_INTEGER) => {
|
|
123
|
+
if (typeof valueArg !== 'number' ||
|
|
124
|
+
!Number.isSafeInteger(valueArg) ||
|
|
125
|
+
Object.is(valueArg, -0) ||
|
|
126
|
+
valueArg < minimumArg ||
|
|
127
|
+
valueArg > maximumArg) {
|
|
128
|
+
return failArg(`${fieldNameArg} must be a safe integer between ${minimumArg} and ${maximumArg}`);
|
|
129
|
+
}
|
|
130
|
+
return valueArg;
|
|
131
|
+
};
|
|
132
|
+
const readBoolean = (valueArg, fieldNameArg) => {
|
|
133
|
+
if (typeof valueArg !== 'boolean') {
|
|
134
|
+
return failArg(`${fieldNameArg} must be a boolean`);
|
|
135
|
+
}
|
|
136
|
+
return valueArg;
|
|
137
|
+
};
|
|
138
|
+
const readLiteral = (valueArg, allowedArg, fieldNameArg) => {
|
|
139
|
+
if (typeof valueArg !== 'string' ||
|
|
140
|
+
!allowedArg.includes(valueArg)) {
|
|
141
|
+
return failArg(`${fieldNameArg} is outside the supported values`);
|
|
142
|
+
}
|
|
143
|
+
return valueArg;
|
|
144
|
+
};
|
|
145
|
+
const readCapacityQuantity = (valueArg, fieldNameArg) => {
|
|
146
|
+
if (typeof valueArg !== 'string' || !capacityPattern.test(valueArg)) {
|
|
147
|
+
return failArg(`${fieldNameArg} must be a canonical base-2 capacity quantity`);
|
|
148
|
+
}
|
|
149
|
+
const numericPart = valueArg.replace(/(?:KiB|MiB|GiB|TiB)$/, '');
|
|
150
|
+
if (!Number.isSafeInteger(Number(numericPart))) {
|
|
151
|
+
return failArg(`${fieldNameArg} exceeds the safe integer quantity range`);
|
|
152
|
+
}
|
|
153
|
+
return valueArg;
|
|
154
|
+
};
|
|
155
|
+
const capacityQuantityBytes = (valueArg) => {
|
|
156
|
+
const units = {
|
|
157
|
+
KiB: 1024n,
|
|
158
|
+
MiB: 1024n ** 2n,
|
|
159
|
+
GiB: 1024n ** 3n,
|
|
160
|
+
TiB: 1024n ** 4n,
|
|
161
|
+
};
|
|
162
|
+
const unit = valueArg.slice(-3);
|
|
163
|
+
return BigInt(valueArg.slice(0, -3)) * units[unit];
|
|
164
|
+
};
|
|
165
|
+
const normalizeCapacityRequest = (valueArg, fieldNameArg) => {
|
|
166
|
+
const value = readRecord(valueArg, fieldNameArg);
|
|
167
|
+
assertKeys(value, ['request'], ['limit'], fieldNameArg);
|
|
168
|
+
const request = readCapacityQuantity(value.request, `${fieldNameArg}.request`);
|
|
169
|
+
const limit = Object.hasOwn(value, 'limit')
|
|
170
|
+
? readCapacityQuantity(value.limit, `${fieldNameArg}.limit`)
|
|
171
|
+
: undefined;
|
|
172
|
+
if (limit && capacityQuantityBytes(limit) < capacityQuantityBytes(request)) {
|
|
173
|
+
return failArg(`${fieldNameArg}.limit must be at least request`);
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
request,
|
|
177
|
+
...(limit ? { limit } : {}),
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
const readEnvironmentKey = (valueArg, fieldNameArg) => {
|
|
181
|
+
if (typeof valueArg !== 'string' ||
|
|
182
|
+
utf8ByteLength(valueArg) >
|
|
183
|
+
storageContractStringLimits.maximumEnvironmentKeyBytes ||
|
|
184
|
+
!environmentKeyPattern.test(valueArg)) {
|
|
185
|
+
return failArg(`${fieldNameArg} must be a canonical environment key`);
|
|
186
|
+
}
|
|
187
|
+
return valueArg;
|
|
188
|
+
};
|
|
189
|
+
const normalizeDelivery = (valueArg, fieldNameArg) => {
|
|
190
|
+
const value = readRecord(valueArg, fieldNameArg);
|
|
191
|
+
if (value.type === 'launcher-environment') {
|
|
192
|
+
assertExactKeys(value, ['type', 'keys'], fieldNameArg);
|
|
193
|
+
const keys = readRecord(value.keys, `${fieldNameArg}.keys`);
|
|
194
|
+
assertKeys(keys, ['endpoint', 'bucket', 'region', 'accessKeyId', 'secretAccessKey'], ['sessionToken'], `${fieldNameArg}.keys`);
|
|
195
|
+
const normalizedKeys = {
|
|
196
|
+
endpoint: readEnvironmentKey(keys.endpoint, `${fieldNameArg}.keys.endpoint`),
|
|
197
|
+
bucket: readEnvironmentKey(keys.bucket, `${fieldNameArg}.keys.bucket`),
|
|
198
|
+
region: readEnvironmentKey(keys.region, `${fieldNameArg}.keys.region`),
|
|
199
|
+
accessKeyId: readEnvironmentKey(keys.accessKeyId, `${fieldNameArg}.keys.accessKeyId`),
|
|
200
|
+
secretAccessKey: readEnvironmentKey(keys.secretAccessKey, `${fieldNameArg}.keys.secretAccessKey`),
|
|
201
|
+
...(Object.hasOwn(keys, 'sessionToken')
|
|
202
|
+
? {
|
|
203
|
+
sessionToken: readEnvironmentKey(keys.sessionToken, `${fieldNameArg}.keys.sessionToken`),
|
|
204
|
+
}
|
|
205
|
+
: {}),
|
|
206
|
+
};
|
|
207
|
+
if (new Set(Object.values(normalizedKeys)).size !==
|
|
208
|
+
Object.keys(normalizedKeys).length) {
|
|
209
|
+
return failArg(`${fieldNameArg}.keys values must be distinct`);
|
|
210
|
+
}
|
|
211
|
+
return { type: 'launcher-environment', keys: normalizedKeys };
|
|
212
|
+
}
|
|
213
|
+
if (value.type === 'file') {
|
|
214
|
+
assertExactKeys(value, ['type', 'targetPath', 'format', 'uid', 'gid', 'mode'], fieldNameArg);
|
|
215
|
+
const targetPath = readString(value.targetPath, `${fieldNameArg}.targetPath`, storageContractStringLimits.maximumSecretPathBytes);
|
|
216
|
+
if (!targetPath.startsWith(`${secretDeliveryRoot}/`) ||
|
|
217
|
+
targetPath.includes('\\') ||
|
|
218
|
+
targetPath.split('/').some((segmentArg) => segmentArg === '.' || segmentArg === '..')) {
|
|
219
|
+
return failArg(`${fieldNameArg}.targetPath must be a canonical path below ${secretDeliveryRoot}`);
|
|
220
|
+
}
|
|
221
|
+
if (value.format !== 'servezone-object-storage') {
|
|
222
|
+
return failArg(`${fieldNameArg}.format is unsupported`);
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
type: 'file',
|
|
226
|
+
targetPath,
|
|
227
|
+
format: 'servezone-object-storage',
|
|
228
|
+
uid: readSafeInteger(value.uid, `${fieldNameArg}.uid`, 0, 2_147_483_647),
|
|
229
|
+
gid: readSafeInteger(value.gid, `${fieldNameArg}.gid`, 0, 2_147_483_647),
|
|
230
|
+
mode: value.mode === 0o400 || value.mode === 0o440
|
|
231
|
+
? value.mode
|
|
232
|
+
: failArg(`${fieldNameArg}.mode must be 0400 or 0440`),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
return failArg(`${fieldNameArg}.type is unsupported`);
|
|
236
|
+
};
|
|
237
|
+
const normalizeObjectStorageProtection = (valueArg, fieldNameArg) => {
|
|
238
|
+
const value = readRecord(valueArg, fieldNameArg);
|
|
239
|
+
assertKeys(value, [], ['backup', 'versioning', 'retentionDays'], fieldNameArg);
|
|
240
|
+
if (Object.keys(value).length === 0) {
|
|
241
|
+
return failArg(`${fieldNameArg} must not be empty`);
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
...(Object.hasOwn(value, 'backup')
|
|
245
|
+
? {
|
|
246
|
+
backup: value.backup === 'required'
|
|
247
|
+
? 'required'
|
|
248
|
+
: failArg(`${fieldNameArg}.backup must be required`),
|
|
249
|
+
}
|
|
250
|
+
: {}),
|
|
251
|
+
...(Object.hasOwn(value, 'versioning')
|
|
252
|
+
? {
|
|
253
|
+
versioning: value.versioning === 'required'
|
|
254
|
+
? 'required'
|
|
255
|
+
: failArg(`${fieldNameArg}.versioning must be required`),
|
|
256
|
+
}
|
|
257
|
+
: {}),
|
|
258
|
+
...(Object.hasOwn(value, 'retentionDays')
|
|
259
|
+
? {
|
|
260
|
+
retentionDays: readSafeInteger(value.retentionDays, `${fieldNameArg}.retentionDays`, 1, 365_000),
|
|
261
|
+
}
|
|
262
|
+
: {}),
|
|
263
|
+
};
|
|
264
|
+
};
|
|
265
|
+
const normalizeFilesystemProtection = (valueArg, fieldNameArg) => {
|
|
266
|
+
const value = readRecord(valueArg, fieldNameArg);
|
|
267
|
+
assertKeys(value, [], ['backup', 'snapshots', 'consistency'], fieldNameArg);
|
|
268
|
+
if (Object.keys(value).length === 0) {
|
|
269
|
+
return failArg(`${fieldNameArg} must not be empty`);
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
...(Object.hasOwn(value, 'backup')
|
|
273
|
+
? {
|
|
274
|
+
backup: value.backup === 'required'
|
|
275
|
+
? 'required'
|
|
276
|
+
: failArg(`${fieldNameArg}.backup must be required`),
|
|
277
|
+
}
|
|
278
|
+
: {}),
|
|
279
|
+
...(Object.hasOwn(value, 'snapshots')
|
|
280
|
+
? {
|
|
281
|
+
snapshots: readLiteral(value.snapshots, ['portable', 'native'], `${fieldNameArg}.snapshots`),
|
|
282
|
+
}
|
|
283
|
+
: {}),
|
|
284
|
+
...(Object.hasOwn(value, 'consistency')
|
|
285
|
+
? {
|
|
286
|
+
consistency: readLiteral(value.consistency, ['crashConsistent', 'applicationConsistent'], `${fieldNameArg}.consistency`),
|
|
287
|
+
}
|
|
288
|
+
: {}),
|
|
289
|
+
};
|
|
290
|
+
};
|
|
291
|
+
const readMountPath = (valueArg, fieldNameArg) => {
|
|
292
|
+
const value = readString(valueArg, fieldNameArg, storageContractStringLimits.maximumSecretPathBytes);
|
|
293
|
+
if (normalizeServiceAbsolutePath(value) !== value || value === '/') {
|
|
294
|
+
return failArg(`${fieldNameArg} must be a canonical non-root absolute path`);
|
|
295
|
+
}
|
|
296
|
+
if (value === secretDeliveryRoot ||
|
|
297
|
+
value.startsWith(`${secretDeliveryRoot}/`) ||
|
|
298
|
+
secretDeliveryRoot.startsWith(`${value}/`)) {
|
|
299
|
+
return failArg(`${fieldNameArg} must not overlap ${secretDeliveryRoot}`);
|
|
300
|
+
}
|
|
301
|
+
return value;
|
|
302
|
+
};
|
|
303
|
+
const normalizeObjectStorageRequest = (valueArg, fieldNameArg) => {
|
|
304
|
+
const value = readRecord(valueArg, fieldNameArg);
|
|
305
|
+
assertKeys(value, [
|
|
306
|
+
'id',
|
|
307
|
+
'kind',
|
|
308
|
+
'storageClass',
|
|
309
|
+
'accessMode',
|
|
310
|
+
'reclaimPolicy',
|
|
311
|
+
'delivery',
|
|
312
|
+
], ['capacity', 'protection'], fieldNameArg);
|
|
313
|
+
if (value.kind !== 'objectStorage') {
|
|
314
|
+
return failArg(`${fieldNameArg}.kind must be objectStorage`);
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
id: readIdentifier(value.id, `${fieldNameArg}.id`),
|
|
318
|
+
kind: 'objectStorage',
|
|
319
|
+
storageClass: readIdentifier(value.storageClass, `${fieldNameArg}.storageClass`),
|
|
320
|
+
accessMode: readLiteral(value.accessMode, ['readOnly', 'readWrite'], `${fieldNameArg}.accessMode`),
|
|
321
|
+
reclaimPolicy: readLiteral(value.reclaimPolicy, ['retain', 'delete'], `${fieldNameArg}.reclaimPolicy`),
|
|
322
|
+
delivery: normalizeDelivery(value.delivery, `${fieldNameArg}.delivery`),
|
|
323
|
+
...(Object.hasOwn(value, 'capacity')
|
|
324
|
+
? {
|
|
325
|
+
capacity: normalizeCapacityRequest(value.capacity, `${fieldNameArg}.capacity`),
|
|
326
|
+
}
|
|
327
|
+
: {}),
|
|
328
|
+
...(Object.hasOwn(value, 'protection')
|
|
329
|
+
? {
|
|
330
|
+
protection: normalizeObjectStorageProtection(value.protection, `${fieldNameArg}.protection`),
|
|
331
|
+
}
|
|
332
|
+
: {}),
|
|
333
|
+
};
|
|
334
|
+
};
|
|
335
|
+
const normalizeFilesystemStorageRequest = (valueArg, fieldNameArg) => {
|
|
336
|
+
const value = readRecord(valueArg, fieldNameArg);
|
|
337
|
+
assertKeys(value, [
|
|
338
|
+
'id',
|
|
339
|
+
'kind',
|
|
340
|
+
'storageClass',
|
|
341
|
+
'accessMode',
|
|
342
|
+
'reclaimPolicy',
|
|
343
|
+
'mountPath',
|
|
344
|
+
], ['capacity', 'protection'], fieldNameArg);
|
|
345
|
+
if (value.kind !== 'filesystem') {
|
|
346
|
+
return failArg(`${fieldNameArg}.kind must be filesystem`);
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
id: readIdentifier(value.id, `${fieldNameArg}.id`),
|
|
350
|
+
kind: 'filesystem',
|
|
351
|
+
storageClass: readIdentifier(value.storageClass, `${fieldNameArg}.storageClass`),
|
|
352
|
+
accessMode: readLiteral(value.accessMode, ['ReadWriteOnce', 'ReadOnlyMany', 'ReadWriteMany'], `${fieldNameArg}.accessMode`),
|
|
353
|
+
reclaimPolicy: readLiteral(value.reclaimPolicy, ['retain', 'delete'], `${fieldNameArg}.reclaimPolicy`),
|
|
354
|
+
mountPath: readMountPath(value.mountPath, `${fieldNameArg}.mountPath`),
|
|
355
|
+
...(Object.hasOwn(value, 'capacity')
|
|
356
|
+
? {
|
|
357
|
+
capacity: normalizeCapacityRequest(value.capacity, `${fieldNameArg}.capacity`),
|
|
358
|
+
}
|
|
359
|
+
: {}),
|
|
360
|
+
...(Object.hasOwn(value, 'protection')
|
|
361
|
+
? {
|
|
362
|
+
protection: normalizeFilesystemProtection(value.protection, `${fieldNameArg}.protection`),
|
|
363
|
+
}
|
|
364
|
+
: {}),
|
|
365
|
+
};
|
|
366
|
+
};
|
|
367
|
+
const normalizeStorageRequest = (valueArg, fieldNameArg) => {
|
|
368
|
+
const value = readRecord(valueArg, fieldNameArg);
|
|
369
|
+
if (value.kind === 'filesystem') {
|
|
370
|
+
return normalizeFilesystemStorageRequest(value, fieldNameArg);
|
|
371
|
+
}
|
|
372
|
+
if (value.kind === 'objectStorage') {
|
|
373
|
+
return normalizeObjectStorageRequest(value, fieldNameArg);
|
|
374
|
+
}
|
|
375
|
+
return failArg(`${fieldNameArg}.kind must be filesystem or objectStorage`);
|
|
376
|
+
};
|
|
377
|
+
return {
|
|
378
|
+
utf8ByteLength,
|
|
379
|
+
readRecord,
|
|
380
|
+
readDenseArray,
|
|
381
|
+
assertKeys,
|
|
382
|
+
assertExactKeys,
|
|
383
|
+
readString,
|
|
384
|
+
readIdentifier,
|
|
385
|
+
readSha256,
|
|
386
|
+
readSafeInteger,
|
|
387
|
+
readBoolean,
|
|
388
|
+
readLiteral,
|
|
389
|
+
readCapacityQuantity,
|
|
390
|
+
capacityQuantityBytes,
|
|
391
|
+
readEnvironmentKey,
|
|
392
|
+
normalizeCapacityRequest,
|
|
393
|
+
normalizeDelivery,
|
|
394
|
+
normalizeObjectStorageRequest,
|
|
395
|
+
normalizeFilesystemStorageRequest,
|
|
396
|
+
normalizeStorageRequest,
|
|
397
|
+
};
|
|
398
|
+
};
|
|
399
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvcmFnZWNvbnRyYWN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvcHJpdmF0ZS9zdG9yYWdlY29udHJhY3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBZUEsT0FBTyxFQUFFLDRCQUE0QixFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFJbEUsb0VBQW9FO0FBQ3BFLE1BQU0sQ0FBQyxNQUFNLDJCQUEyQixHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDdkQsNEJBQTRCLEVBQUUsSUFBSTtJQUNsQywwQkFBMEIsRUFBRSxHQUFHO0lBQy9CLHNCQUFzQixFQUFFLElBQUk7Q0FDN0IsQ0FBQyxDQUFDO0FBRUgsTUFBTSxrQkFBa0IsR0FBRyxzQ0FBc0MsQ0FBQztBQUNsRSxNQUFNLHFCQUFxQixHQUFHLDBCQUEwQixDQUFDO0FBQ3pELE1BQU0sYUFBYSxHQUFHLGdCQUFnQixDQUFDO0FBQ3ZDLE1BQU0sZUFBZSxHQUFHLGtDQUFrQyxDQUFDO0FBQzNEOzs7R0FHRztBQUNILE1BQU0sa0JBQWtCLEdBQUcsY0FBYyxDQUFDO0FBRTFDLE1BQU0sQ0FBQyxNQUFNLDRCQUE0QixHQUFHLENBQUMsT0FBOEIsRUFBRSxFQUFFO0lBQzdFLE1BQU0sY0FBYyxHQUFHLENBQUMsUUFBZ0IsRUFBVSxFQUFFO1FBQ2xELE9BQU8sSUFBSSxXQUFXLEVBQUUsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsVUFBVSxDQUFDO0lBQ3ZELENBQUMsQ0FBQztJQUVGLE1BQU0sVUFBVSxHQUFHLENBQ2pCLFFBQWlCLEVBQ2pCLFlBQW9CLEVBQ0ssRUFBRTtRQUMzQixJQUFJLENBQUMsUUFBUSxJQUFJLE9BQU8sUUFBUSxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7WUFDekUsT0FBTyxPQUFPLENBQUMsR0FBRyxZQUFZLHlCQUF5QixDQUFDLENBQUM7UUFDM0QsQ0FBQztRQUNELE1BQU0sU0FB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
|
|
@@ -5,6 +5,7 @@ import type { IClusterNode, INodeAction, INodeMetricsSample, TNodeActionName } f
|
|
|
5
5
|
import type { IDeployment } from '../data/deployment.js';
|
|
6
6
|
import type { IIdentity } from '../data/user.js';
|
|
7
7
|
import type { INodeCredentialMetadata, INodeCredentialMutationPreconditions, INodeCredentialMutationResult, TNodeCredentialPurpose } from '../data/nodecredential.js';
|
|
8
|
+
import type { INodeRuntimeRetirement } from '../data/noderetirement.js';
|
|
8
9
|
import type { INodeEnrollmentRequest, INodeEnrollmentAcknowledgement, INodeEnrollmentState } from '../data/nodeenrollment.js';
|
|
9
10
|
/** Node-self adoption preconditions, not an admin or workload RBAC grant. */
|
|
10
11
|
export interface IReq_Spark_Cloudly_GetNodeEnrollmentState extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Spark_Cloudly_GetNodeEnrollmentState> {
|
|
@@ -150,6 +151,30 @@ export interface IReq_Any_Cloudly_DeleteNodeById extends plugins.typedrequestInt
|
|
|
150
151
|
ok: boolean;
|
|
151
152
|
};
|
|
152
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* An administrator retires one node from its cluster's Pallet runtime.
|
|
156
|
+
*
|
|
157
|
+
* Requires verified platform-infrastructure admin authority. This is the step that makes a node
|
|
158
|
+
* deletable: it writes the retirement row, on which the producer stops and removes the node's
|
|
159
|
+
* slots, the compiler quarantines its handoff, and both node credentials are revoked. Cloudly
|
|
160
|
+
* answers the row as it stands, which is `retiring` until all of that has settled.
|
|
161
|
+
*
|
|
162
|
+
* `expectedGeneration` is the cluster runtime generation the caller read with `getClusterRuntime`:
|
|
163
|
+
* retirement is a statement about a node of a Pallet cluster, so a cutover that moved the phase
|
|
164
|
+
* meanwhile refuses the write instead of retiring a node out of a runtime it no longer runs in.
|
|
165
|
+
* Every refusal is one of `data.nodeRuntimeRetirementRefusals` and takes nothing away.
|
|
166
|
+
*/
|
|
167
|
+
export interface IReq_Any_Cloudly_RetireClusterNode extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_RetireClusterNode> {
|
|
168
|
+
method: 'retireClusterNode';
|
|
169
|
+
request: {
|
|
170
|
+
identity: IIdentity;
|
|
171
|
+
nodeId: string;
|
|
172
|
+
expectedGeneration: number;
|
|
173
|
+
};
|
|
174
|
+
response: {
|
|
175
|
+
retirement: INodeRuntimeRetirement;
|
|
176
|
+
};
|
|
177
|
+
}
|
|
153
178
|
/**
|
|
154
179
|
* An administrator states where one node's Corestore control API answers.
|
|
155
180
|
*
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -892,9 +892,14 @@ on one spec revision, and each reason belongs to exactly one state. `converged`
|
|
|
892
892
|
carry `reason: null`. `waiting` carries a `TServiceRuntimeWaitingReason`: `node-network-pending`,
|
|
893
893
|
`conflict` or `failure-backoff`. `refused` carries a `TServiceRuntimeRefusal`:
|
|
894
894
|
`cluster-runtime-missing`, `organization-missing`, `image-plan-missing`, `platform-unsupported`,
|
|
895
|
-
`secrets-unsupported`, `volumes-unsupported`, `port-protocol-unsupported`,
|
|
896
|
-
`readiness-target-undeclared`, `environment-too-large`, `no-eligible-node
|
|
897
|
-
|
|
895
|
+
`secrets-unsupported`, `volumes-unsupported`, `port-protocol-unsupported`, `ports-too-many`,
|
|
896
|
+
`readiness-target-undeclared`, `environment-too-large`, `no-eligible-node`, `replica-limit` or
|
|
897
|
+
`node-authority-unsupported`. `ports-too-many` is a service declaring more target ports than the
|
|
898
|
+
`runtimeConfigContract.maximumTargetPorts` (64) a config carries, where `port-protocol-unsupported`
|
|
899
|
+
is about one port's protocol; `node-authority-unsupported` is a node whose session resolved fewer
|
|
900
|
+
workload authorities than the spec needs, refused at the admit step rather than handed to a node
|
|
901
|
+
that would silently park it. A refusal never retires running slots.
|
|
902
|
+
`snapshotServiceRuntimeStatus` enforces the pairing.
|
|
898
903
|
|
|
899
904
|
### Runtime Assignment Views
|
|
900
905
|
|
|
@@ -911,6 +916,23 @@ through every slot on a cluster's nodes; `limit` runs from 1 to
|
|
|
911
916
|
`requests.service.pushServiceRuntimeChanged { serviceId }` tells admin UIs that a service's spec,
|
|
912
917
|
status or assignments changed; it carries no state, so a late push never overwrites a newer read.
|
|
913
918
|
|
|
919
|
+
### Node Retirement
|
|
920
|
+
|
|
921
|
+
`data.INodeRuntimeRetirement { id, clusterId, state, actorId, since }` records that one node leaves
|
|
922
|
+
its cluster's Pallet runtime. `TNodeRuntimeRetirementState` is `retiring` while the producer still
|
|
923
|
+
stops and removes the node's slots, the compiler quarantines its handoff and both node credentials
|
|
924
|
+
are revoked, and `retired` once that has settled. It is stored beside the node document, because a
|
|
925
|
+
node update merges caller data and no caller may retire a node with it. The producers read the row's
|
|
926
|
+
presence rather than its state: a node that has one is no longer an egress member and desires no
|
|
927
|
+
slots, so the row is written before anything is taken away and stays as long as the node document.
|
|
928
|
+
`snapshotNodeRuntimeRetirement` checks the record.
|
|
929
|
+
|
|
930
|
+
`requests.node.retireClusterNode { identity, nodeId, expectedGeneration }` answers `{ retirement }`.
|
|
931
|
+
`expectedGeneration` is the cluster runtime generation the caller read with `getClusterRuntime`, so a
|
|
932
|
+
cutover that moved the phase meanwhile refuses the write. Every refusal is one of
|
|
933
|
+
`data.nodeRuntimeRetirementRefusals` — `node-missing`, `cluster-runtime-not-pallet`,
|
|
934
|
+
`node-already-retired` or `conflict` — and takes nothing away.
|
|
935
|
+
|
|
914
936
|
### Corestore Database Export Packaging
|
|
915
937
|
|
|
916
938
|
The Node-only `@serve.zone/interfaces/runtime` export provides metadata-only
|
|
@@ -1271,6 +1293,21 @@ metadata plus a service-owned credential management scope and delivery policy,
|
|
|
1271
1293
|
never credential references or values. Filesystem
|
|
1272
1294
|
bindings expose the container mount and access mode, never a host path.
|
|
1273
1295
|
|
|
1296
|
+
A binding's `requestDigest` is stated by one function, so the control plane that
|
|
1297
|
+
issues it and the consumer that re-derives it compute the same value:
|
|
1298
|
+
|
|
1299
|
+
```typescript
|
|
1300
|
+
const requestDigest = await platform.createStorageRequestSha256(storageRequest);
|
|
1301
|
+
```
|
|
1302
|
+
|
|
1303
|
+
It normalizes the request to its contract shape, canonicalizes it and returns
|
|
1304
|
+
bare lowercase hex over the request alone — no prefix, and nothing wrapped
|
|
1305
|
+
around the request. A body that is not a storage request is refused with
|
|
1306
|
+
`platform.StorageContractError` instead of hashed, and
|
|
1307
|
+
`platform.canonicalizeStorageRequest` returns the exact bytes the digest is
|
|
1308
|
+
taken over. `platform.storageRequestCanonicalDigestGoldenVectors` states both
|
|
1309
|
+
for a filesystem request and for both object-storage deliveries.
|
|
1310
|
+
|
|
1274
1311
|
Portable manifests and resolved bindings intentionally have no fields for
|
|
1275
1312
|
Synology, NFS, Kerberos, Corestore, Kubernetes, Docker drivers, servers,
|
|
1276
1313
|
exports, mount options, provider credential values, or local fallback paths. Runtimes
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/data/index.ts
CHANGED
|
@@ -25,6 +25,7 @@ export * from './isolatedrestore.golden.js';
|
|
|
25
25
|
export * from './mail.js';
|
|
26
26
|
export * from './nodecredential.js';
|
|
27
27
|
export * from './nodeenrollment.js';
|
|
28
|
+
export * from './noderetirement.js';
|
|
28
29
|
export * from './networkaddressplan.js';
|
|
29
30
|
export * from './networkauthority.js';
|
|
30
31
|
export * from './networkcredential.js';
|