@serve.zone/interfaces 31.5.0 → 31.7.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 +68 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/backup.d.ts +13 -0
- package/dist_ts/data/clusternode.d.ts +49 -0
- package/dist_ts/data/clusternode.js +47 -1
- package/dist_ts/data/corestore.d.ts +27 -0
- package/dist_ts/data/corestore.js +11 -0
- package/dist_ts/data/index.d.ts +1 -0
- package/dist_ts/data/index.js +2 -1
- package/dist_ts/data/isolatedrestore.js +42 -2
- package/dist_ts/requests/backup.d.ts +223 -0
- package/dist_ts/requests/corestore.d.ts +57 -0
- package/dist_ts/requests/corestore.js +102 -1
- package/dist_ts/requests/node.d.ts +31 -0
- package/dist_ts/requests/platform.d.ts +30 -0
- package/dist_ts/runtime.corestore.d.ts +7 -10
- package/dist_ts/runtime.corestore.js +1 -1
- package/dist_ts/runtime.corestore.relay.d.ts +47 -0
- package/dist_ts/runtime.corestore.relay.js +57 -0
- package/dist_ts/runtime.d.ts +1 -0
- package/dist_ts/runtime.js +2 -1
- package/package.json +1 -1
- package/readme.md +112 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/backup.ts +14 -0
- package/ts/data/clusternode.ts +93 -0
- package/ts/data/corestore.ts +28 -0
- package/ts/data/index.ts +1 -0
- package/ts/data/isolatedrestore.ts +53 -1
- package/ts/requests/backup.ts +253 -0
- package/ts/requests/corestore.ts +183 -0
- package/ts/requests/node.ts +35 -0
- package/ts/requests/platform.ts +36 -0
- package/ts/runtime.corestore.relay.ts +111 -0
- package/ts/runtime.corestore.ts +7 -8
- package/ts/runtime.ts +1 -0
|
@@ -2,6 +2,7 @@ import * as plugins from '../plugins.js';
|
|
|
2
2
|
import type { IArchivePruneResult, IArchiveRetentionPolicy, IBackupArchiveManifest, IBackupArchiveObject, IBackupRecord, IBackupReplicationResult, IIsolatedRestoreNodeState, IIsolatedRestoreRecord, TBackupSnapshot, TIsolatedRestoreResourceMapping } from '../data/backup.js';
|
|
3
3
|
import type { IService } from '../data/service.js';
|
|
4
4
|
import type { IIdentity, IIdentityCredential } from '../data/user.js';
|
|
5
|
+
import type { ICorestoreDatabaseClosureDescriptor } from '../runtime.corestore.relay.js';
|
|
5
6
|
export interface IReq_Any_Cloudly_CreateServiceBackup extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_CreateServiceBackup> {
|
|
6
7
|
method: 'createServiceBackup';
|
|
7
8
|
request: {
|
|
@@ -48,6 +49,15 @@ export interface IReq_Any_Cloudly_RestoreServiceBackup extends plugins.typedrequ
|
|
|
48
49
|
backup: IBackupRecord;
|
|
49
50
|
};
|
|
50
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Superseded by `backupClusterService`, which names the node.
|
|
54
|
+
*
|
|
55
|
+
* This method was dispatched at the coreflow that had tagged itself with the
|
|
56
|
+
* node's hostname; a cluster relay authors no tags and serves every node of its
|
|
57
|
+
* cluster, so the node it should act on can no longer be inferred from which
|
|
58
|
+
* connection answered. It is retired together with the tag in the coordinated
|
|
59
|
+
* major.
|
|
60
|
+
*/
|
|
51
61
|
export interface IReq_Cloudly_Coreflow_ExecuteServiceBackup extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Coreflow_ExecuteServiceBackup> {
|
|
52
62
|
method: 'executeServiceBackup';
|
|
53
63
|
request: {
|
|
@@ -63,6 +73,7 @@ export interface IReq_Cloudly_Coreflow_ExecuteServiceBackup extends plugins.type
|
|
|
63
73
|
replication?: IBackupReplicationResult;
|
|
64
74
|
};
|
|
65
75
|
}
|
|
76
|
+
/** Superseded by `restoreClusterService`; see `executeServiceBackup`. */
|
|
66
77
|
export interface IReq_Cloudly_Coreflow_ExecuteServiceRestore extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Coreflow_ExecuteServiceRestore> {
|
|
67
78
|
method: 'executeServiceRestore';
|
|
68
79
|
request: {
|
|
@@ -90,6 +101,14 @@ export interface IReq_Coreflow_Cloudly_PrepareBackupReplication extends plugins.
|
|
|
90
101
|
missingObjects: IBackupArchiveObject[];
|
|
91
102
|
};
|
|
92
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Retiring: `contentsBase64` carries a whole archive object inside one JSON
|
|
106
|
+
* envelope, which costs a third of the payload in encoding and bounds a backup
|
|
107
|
+
* by the message ceiling rather than by the transport. `backupServiceDatabaseClosure`
|
|
108
|
+
* streams the same bytes as a VirtualStream. Removal is the coordinated major,
|
|
109
|
+
* once every relay and every Cloudly speak the streamed contract; Corestore
|
|
110
|
+
* already documents its repository-wide archive object routes as legacy.
|
|
111
|
+
*/
|
|
93
112
|
export interface IReq_Coreflow_Cloudly_UploadBackupArchiveObject extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Coreflow_Cloudly_UploadBackupArchiveObject> {
|
|
94
113
|
method: 'uploadBackupArchiveObject';
|
|
95
114
|
request: {
|
|
@@ -123,6 +142,7 @@ export interface IReq_Coreflow_Cloudly_GetBackupArchiveManifest extends plugins.
|
|
|
123
142
|
manifest: IBackupArchiveManifest;
|
|
124
143
|
};
|
|
125
144
|
}
|
|
145
|
+
/** Retiring with `uploadBackupArchiveObject`; `restoreServiceDatabaseClosure` replaces it. */
|
|
126
146
|
export interface IReq_Coreflow_Cloudly_DownloadBackupArchiveObject extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Coreflow_Cloudly_DownloadBackupArchiveObject> {
|
|
127
147
|
method: 'downloadBackupArchiveObject';
|
|
128
148
|
request: {
|
|
@@ -135,6 +155,7 @@ export interface IReq_Coreflow_Cloudly_DownloadBackupArchiveObject extends plugi
|
|
|
135
155
|
contentsBase64: string;
|
|
136
156
|
};
|
|
137
157
|
}
|
|
158
|
+
/** Superseded by `pruneClusterNodeArchive`; see `executeServiceBackup`. */
|
|
138
159
|
export interface IReq_Cloudly_Coreflow_PruneNodeArchive extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Coreflow_PruneNodeArchive> {
|
|
139
160
|
method: 'coreflowPruneNodeArchive';
|
|
140
161
|
request: {
|
|
@@ -146,6 +167,127 @@ export interface IReq_Cloudly_Coreflow_PruneNodeArchive extends plugins.typedreq
|
|
|
146
167
|
result?: IArchivePruneResult;
|
|
147
168
|
};
|
|
148
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Backs up one service on one exact node of this cluster.
|
|
172
|
+
*
|
|
173
|
+
* The cluster relay serves every node of its cluster over one session, so the
|
|
174
|
+
* node is a request field: the connection that answers proves which cluster is
|
|
175
|
+
* speaking, never which node is meant. Everything else is what
|
|
176
|
+
* `executeServiceBackup` carried, so a caller moving to this method changes its
|
|
177
|
+
* routing and nothing about its backup.
|
|
178
|
+
*/
|
|
179
|
+
export interface IReq_Cloudly_Relay_BackupClusterService extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Relay_BackupClusterService> {
|
|
180
|
+
method: 'backupClusterService';
|
|
181
|
+
request: {
|
|
182
|
+
/** The cluster node whose Corestore holds this service's data. */
|
|
183
|
+
nodeId: string;
|
|
184
|
+
backupId: string;
|
|
185
|
+
service: IService;
|
|
186
|
+
tags?: Record<string, string>;
|
|
187
|
+
replication?: {
|
|
188
|
+
enabled: boolean;
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
response: {
|
|
192
|
+
snapshots: TBackupSnapshot[];
|
|
193
|
+
replication?: IBackupReplicationResult;
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/** Restores one service on one exact node of this cluster; see `backupClusterService`. */
|
|
197
|
+
export interface IReq_Cloudly_Relay_RestoreClusterService extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Relay_RestoreClusterService> {
|
|
198
|
+
method: 'restoreClusterService';
|
|
199
|
+
request: {
|
|
200
|
+
/** The cluster node whose Corestore receives the restore. */
|
|
201
|
+
nodeId: string;
|
|
202
|
+
backupId: string;
|
|
203
|
+
service: IService;
|
|
204
|
+
snapshots: TBackupSnapshot[];
|
|
205
|
+
clear?: boolean;
|
|
206
|
+
resourceTypes?: Array<TBackupSnapshot['type']>;
|
|
207
|
+
replication?: {
|
|
208
|
+
enabled: boolean;
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
response: {
|
|
212
|
+
restored: TBackupSnapshot[];
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Prunes the archive of one exact node of this cluster.
|
|
217
|
+
*
|
|
218
|
+
* `found` stays false when that node has no archive to prune, which is a fact
|
|
219
|
+
* about the node rather than a failure of the relay.
|
|
220
|
+
*/
|
|
221
|
+
export interface IReq_Cloudly_Relay_PruneClusterNodeArchive extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Relay_PruneClusterNodeArchive> {
|
|
222
|
+
method: 'pruneClusterNodeArchive';
|
|
223
|
+
request: {
|
|
224
|
+
/** The cluster node whose archive is pruned. */
|
|
225
|
+
nodeId: string;
|
|
226
|
+
retention: IArchiveRetentionPolicy;
|
|
227
|
+
dryRun?: boolean;
|
|
228
|
+
};
|
|
229
|
+
response: {
|
|
230
|
+
found: boolean;
|
|
231
|
+
result?: IArchivePruneResult;
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* The relay carries one service database closure to Cloudly.
|
|
236
|
+
*
|
|
237
|
+
* Relay-initiated, like every other byte a cluster sends: a cluster is
|
|
238
|
+
* outbound-only, so Cloudly cannot pull from it. The relay exports the closure
|
|
239
|
+
* from its node's Corestore and sends those exact bytes on its own session.
|
|
240
|
+
*
|
|
241
|
+
* `accepted` permits sending; it never confirms storage. The stream's own
|
|
242
|
+
* acceptance receipt, observed through `completion`, is what confirms that
|
|
243
|
+
* Cloudly durably holds the closure — exactly as `pushImageVersion` works.
|
|
244
|
+
*
|
|
245
|
+
* No identity field: the cluster is the one the verified machine identity on
|
|
246
|
+
* the relay's own session names, exactly as `getClusterRouteTable` and
|
|
247
|
+
* `getClusterPlatformDesiredState` work. A request-level identity would be a
|
|
248
|
+
* second authority for the same fact, and the weaker of the two.
|
|
249
|
+
*/
|
|
250
|
+
export interface IReq_Relay_Cloudly_BackupServiceDatabaseClosure extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Relay_Cloudly_BackupServiceDatabaseClosure> {
|
|
251
|
+
method: 'backupServiceDatabaseClosure';
|
|
252
|
+
request: {
|
|
253
|
+
backupId: string;
|
|
254
|
+
serviceId: string;
|
|
255
|
+
/** The cluster node this closure was exported from. */
|
|
256
|
+
nodeId: string;
|
|
257
|
+
/** Exact bytes and Corestore's receipt for them; validated before any byte moves. */
|
|
258
|
+
descriptor: ICorestoreDatabaseClosureDescriptor;
|
|
259
|
+
/** Requester sends closure bytes; the TypedHandler receives the opposite endpoint. */
|
|
260
|
+
closure: plugins.typedrequestInterfaces.TVirtualStream<'send'>;
|
|
261
|
+
};
|
|
262
|
+
response: {
|
|
263
|
+
accepted: boolean;
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* The relay fetches one service database closure back from Cloudly.
|
|
268
|
+
*
|
|
269
|
+
* The descriptor arrives with the stream rather than after it, because
|
|
270
|
+
* Corestore's restore needs the exact byte length and receipt before the first
|
|
271
|
+
* byte is written: the relay opens its Corestore request from this descriptor
|
|
272
|
+
* and only then drains the stream into it.
|
|
273
|
+
*
|
|
274
|
+
* No identity field, for the same reason as `backupServiceDatabaseClosure`: the
|
|
275
|
+
* relay's own session names the cluster.
|
|
276
|
+
*/
|
|
277
|
+
export interface IReq_Relay_Cloudly_RestoreServiceDatabaseClosure extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Relay_Cloudly_RestoreServiceDatabaseClosure> {
|
|
278
|
+
method: 'restoreServiceDatabaseClosure';
|
|
279
|
+
request: {
|
|
280
|
+
backupId: string;
|
|
281
|
+
serviceId: string;
|
|
282
|
+
/** The cluster node the closure is restored onto. */
|
|
283
|
+
nodeId: string;
|
|
284
|
+
};
|
|
285
|
+
response: {
|
|
286
|
+
descriptor: ICorestoreDatabaseClosureDescriptor;
|
|
287
|
+
/** Requester receives closure bytes; the TypedHandler returns a sender endpoint. */
|
|
288
|
+
closure: plugins.typedrequestInterfaces.TVirtualStream<'receive'>;
|
|
289
|
+
};
|
|
290
|
+
}
|
|
149
291
|
/**
|
|
150
292
|
* Creates a restore in a new non-runnable data-plane scratch namespace.
|
|
151
293
|
* Cloudly derives clusterId and sourceServiceId from the authoritative backup,
|
|
@@ -225,6 +367,18 @@ export interface IReq_Any_Cloudly_GetIsolatedRestoreById extends plugins.typedre
|
|
|
225
367
|
export interface IReq_Cloudly_Coreflow_PrepareIsolatedRestore extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Coreflow_PrepareIsolatedRestore> {
|
|
226
368
|
method: 'coreflowPrepareIsolatedRestore';
|
|
227
369
|
request: {
|
|
370
|
+
/**
|
|
371
|
+
* The compact RS256 restore grant Cloudly signed for exactly this
|
|
372
|
+
* operation, carried untouched to Corestore.
|
|
373
|
+
*
|
|
374
|
+
* Corestore accepts no other authority on an isolated-restore route: the
|
|
375
|
+
* grant names the operation, expires within minutes, and binds the cluster,
|
|
376
|
+
* the node, the canonical resource mappings and the archive manifest, so a
|
|
377
|
+
* relay can carry it but can neither widen it nor mint one. Sensitive
|
|
378
|
+
* ephemeral input: exclude the complete request from hooks, logs and
|
|
379
|
+
* journals on every hop.
|
|
380
|
+
*/
|
|
381
|
+
restoreGrant: string;
|
|
228
382
|
clusterId: string;
|
|
229
383
|
targetNodeName: string;
|
|
230
384
|
restoreId: string;
|
|
@@ -240,10 +394,55 @@ export interface IReq_Cloudly_Coreflow_PrepareIsolatedRestore extends plugins.ty
|
|
|
240
394
|
state: IIsolatedRestoreNodeState;
|
|
241
395
|
};
|
|
242
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* Cloudly stages one archive object of an isolated restore on the relay's node.
|
|
399
|
+
*
|
|
400
|
+
* Cloudly holds the source backup, so these bytes travel Cloudly → relay and
|
|
401
|
+
* the requester is the sender. One object per call, as the archive manifest
|
|
402
|
+
* lists it: Corestore writes staged objects under a `write-object` grant, one
|
|
403
|
+
* bounded chunk at a time, and the relay is what turns this stream into those
|
|
404
|
+
* chunks. The object descriptor is the manifest entry itself — path, exact size
|
|
405
|
+
* and digest — so the relay can prove the complete object before it counts as
|
|
406
|
+
* written, and so a replayed stage settles on the same bytes.
|
|
407
|
+
*/
|
|
408
|
+
export interface IReq_Cloudly_Relay_StageIsolatedRestoreArchive extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Relay_StageIsolatedRestoreArchive> {
|
|
409
|
+
method: 'stageIsolatedRestoreArchive';
|
|
410
|
+
request: {
|
|
411
|
+
/**
|
|
412
|
+
* The compact RS256 restore grant for the `write-object` operation, carried
|
|
413
|
+
* untouched to Corestore. Sensitive ephemeral input, like every other
|
|
414
|
+
* grant on these routes.
|
|
415
|
+
*/
|
|
416
|
+
restoreGrant: string;
|
|
417
|
+
clusterId: string;
|
|
418
|
+
targetNodeName: string;
|
|
419
|
+
restoreId: string;
|
|
420
|
+
expectedRevision: number;
|
|
421
|
+
/** The manifest entry these bytes complete: `path`, `size`, `sha256`. */
|
|
422
|
+
object: IBackupArchiveObject;
|
|
423
|
+
/** Requester sends the object bytes; the TypedHandler receives them. */
|
|
424
|
+
archive: plugins.typedrequestInterfaces.TVirtualStream<'send'>;
|
|
425
|
+
};
|
|
426
|
+
response: {
|
|
427
|
+
state: IIsolatedRestoreNodeState;
|
|
428
|
+
};
|
|
429
|
+
}
|
|
243
430
|
/** Read-only status for one exact cluster/node/restore routing key. */
|
|
244
431
|
export interface IReq_Cloudly_Coreflow_GetIsolatedRestoreStatus extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Coreflow_GetIsolatedRestoreStatus> {
|
|
245
432
|
method: 'coreflowGetIsolatedRestoreStatus';
|
|
246
433
|
request: {
|
|
434
|
+
/**
|
|
435
|
+
* The compact RS256 restore grant Cloudly signed for exactly this
|
|
436
|
+
* operation, carried untouched to Corestore.
|
|
437
|
+
*
|
|
438
|
+
* Corestore accepts no other authority on an isolated-restore route: the
|
|
439
|
+
* grant names the operation, expires within minutes, and binds the cluster,
|
|
440
|
+
* the node, the canonical resource mappings and the archive manifest, so a
|
|
441
|
+
* relay can carry it but can neither widen it nor mint one. Sensitive
|
|
442
|
+
* ephemeral input: exclude the complete request from hooks, logs and
|
|
443
|
+
* journals on every hop.
|
|
444
|
+
*/
|
|
445
|
+
restoreGrant: string;
|
|
247
446
|
clusterId: string;
|
|
248
447
|
targetNodeName: string;
|
|
249
448
|
restoreId: string;
|
|
@@ -256,6 +455,18 @@ export interface IReq_Cloudly_Coreflow_GetIsolatedRestoreStatus extends plugins.
|
|
|
256
455
|
export interface IReq_Cloudly_Coreflow_ExecuteIsolatedRestore extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Coreflow_ExecuteIsolatedRestore> {
|
|
257
456
|
method: 'coreflowExecuteIsolatedRestore';
|
|
258
457
|
request: {
|
|
458
|
+
/**
|
|
459
|
+
* The compact RS256 restore grant Cloudly signed for exactly this
|
|
460
|
+
* operation, carried untouched to Corestore.
|
|
461
|
+
*
|
|
462
|
+
* Corestore accepts no other authority on an isolated-restore route: the
|
|
463
|
+
* grant names the operation, expires within minutes, and binds the cluster,
|
|
464
|
+
* the node, the canonical resource mappings and the archive manifest, so a
|
|
465
|
+
* relay can carry it but can neither widen it nor mint one. Sensitive
|
|
466
|
+
* ephemeral input: exclude the complete request from hooks, logs and
|
|
467
|
+
* journals on every hop.
|
|
468
|
+
*/
|
|
469
|
+
restoreGrant: string;
|
|
259
470
|
clusterId: string;
|
|
260
471
|
targetNodeName: string;
|
|
261
472
|
restoreId: string;
|
|
@@ -272,6 +483,18 @@ export interface IReq_Cloudly_Coreflow_ExecuteIsolatedRestore extends plugins.ty
|
|
|
272
483
|
export interface IReq_Cloudly_Coreflow_CleanupIsolatedRestore extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Coreflow_CleanupIsolatedRestore> {
|
|
273
484
|
method: 'coreflowCleanupIsolatedRestore';
|
|
274
485
|
request: {
|
|
486
|
+
/**
|
|
487
|
+
* The compact RS256 restore grant Cloudly signed for exactly this
|
|
488
|
+
* operation, carried untouched to Corestore.
|
|
489
|
+
*
|
|
490
|
+
* Corestore accepts no other authority on an isolated-restore route: the
|
|
491
|
+
* grant names the operation, expires within minutes, and binds the cluster,
|
|
492
|
+
* the node, the canonical resource mappings and the archive manifest, so a
|
|
493
|
+
* relay can carry it but can neither widen it nor mint one. Sensitive
|
|
494
|
+
* ephemeral input: exclude the complete request from hooks, logs and
|
|
495
|
+
* journals on every hop.
|
|
496
|
+
*/
|
|
497
|
+
restoreGrant: string;
|
|
275
498
|
clusterId: string;
|
|
276
499
|
targetNodeName: string;
|
|
277
500
|
restoreId: string;
|
|
@@ -19,6 +19,10 @@ export interface ICorestoreServiceResources {
|
|
|
19
19
|
/**
|
|
20
20
|
* Corestore inventory of one node (each coreflow node runs its own corestore;
|
|
21
21
|
* the data is node-local, which is what makes node placement matter).
|
|
22
|
+
*
|
|
23
|
+
* Superseded by `IClusterCorestoreInventory`, which names the node by its
|
|
24
|
+
* cluster-document id instead of by the Swarm hostnames a connection tagged
|
|
25
|
+
* itself with.
|
|
22
26
|
*/
|
|
23
27
|
export interface ICorestoreNodeInventory {
|
|
24
28
|
/** Swarm node hostnames of the reporting coreflow. */
|
|
@@ -63,6 +67,35 @@ export interface ICoreflowInventoryRegistration {
|
|
|
63
67
|
reporterSessionId: string;
|
|
64
68
|
registeredAt: number;
|
|
65
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* What one node's Corestore holds, as the cluster relay read it.
|
|
72
|
+
*
|
|
73
|
+
* Node identity is the cluster document's node id: one relay serves every node
|
|
74
|
+
* of its cluster, so which node an inventory describes is stated, never derived
|
|
75
|
+
* from which connection answered. An unreachable node names why — `errorCode`
|
|
76
|
+
* is required whenever `reachable` is false and refused whenever it is true —
|
|
77
|
+
* because a node whose Corestore cannot be reached is a fact the control plane
|
|
78
|
+
* needs, and a silent empty inventory would read as "nothing provisioned",
|
|
79
|
+
* which is the opposite.
|
|
80
|
+
*/
|
|
81
|
+
export interface IClusterCorestoreInventory {
|
|
82
|
+
nodeId: string;
|
|
83
|
+
checkedAt: number;
|
|
84
|
+
reachable: boolean;
|
|
85
|
+
errorCode?: 'CORESTORE_ENDPOINT_UNKNOWN' | 'CORESTORE_UNREACHABLE';
|
|
86
|
+
services: ICorestoreServiceResources[];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Validates one cluster Corestore inventory and names every reason it is
|
|
90
|
+
* refused.
|
|
91
|
+
*
|
|
92
|
+
* Shape only. Whether that node belongs to the cluster, and whether a resource
|
|
93
|
+
* should exist, are decisions for the control plane that asked; an inventory
|
|
94
|
+
* that passes here has claimed neither. A reachable inventory that names an
|
|
95
|
+
* error, and an unreachable one that lists resources, are both refused: an
|
|
96
|
+
* answer has to be one of the two things it can be.
|
|
97
|
+
*/
|
|
98
|
+
export declare const validateClusterCorestoreInventory: (valueArg: unknown, pathArg?: string) => string[];
|
|
66
99
|
export interface IReq_Any_Cloudly_GetCorestoreInventory extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_GetCorestoreInventory> {
|
|
67
100
|
method: 'getCorestoreInventory';
|
|
68
101
|
request: {
|
|
@@ -70,6 +103,30 @@ export interface IReq_Any_Cloudly_GetCorestoreInventory extends plugins.typedreq
|
|
|
70
103
|
};
|
|
71
104
|
response: ICorestoreInventoryReport;
|
|
72
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Cloudly asks the relay what one exact node of its cluster holds.
|
|
108
|
+
*
|
|
109
|
+
* The node is a request field because one relay serves them all: the connection
|
|
110
|
+
* that answers proves the cluster, never the node. A greenfield cluster answers
|
|
111
|
+
* with an empty `services` list and `reachable: true`, which is the proof that
|
|
112
|
+
* a namespace is blank — distinct from `reachable: false`, which proves
|
|
113
|
+
* nothing at all.
|
|
114
|
+
*
|
|
115
|
+
* Named apart from `getCorestoreInventory` on purpose: that method is Cloudly's
|
|
116
|
+
* own operator-facing read and lives on the other end of this socket. One name
|
|
117
|
+
* on both ends would leave direction as the only thing telling two different
|
|
118
|
+
* bodies apart.
|
|
119
|
+
*/
|
|
120
|
+
export interface IReq_Cloudly_Relay_GetClusterCorestoreInventory extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Relay_GetClusterCorestoreInventory> {
|
|
121
|
+
method: 'getClusterCorestoreInventory';
|
|
122
|
+
request: {
|
|
123
|
+
nodeId: string;
|
|
124
|
+
};
|
|
125
|
+
response: {
|
|
126
|
+
inventory: IClusterCorestoreInventory;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/** Superseded by `getClusterCorestoreInventory`, which names the node. */
|
|
73
130
|
export interface IReq_Cloudly_Coreflow_GetCorestoreInventory extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Coreflow_GetCorestoreInventory> {
|
|
74
131
|
method: 'coreflowGetCorestoreInventory';
|
|
75
132
|
request: Record<string, never>;
|
|
@@ -1,2 +1,103 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
|
-
|
|
2
|
+
const clusterCorestoreInventoryIdentifierPattern = /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/;
|
|
3
|
+
const isClusterCorestoreRecord = (valueArg) => (Boolean(valueArg)
|
|
4
|
+
&& typeof valueArg === 'object'
|
|
5
|
+
&& !Array.isArray(valueArg)
|
|
6
|
+
&& (Object.getPrototypeOf(valueArg) === Object.prototype
|
|
7
|
+
|| Object.getPrototypeOf(valueArg) === null));
|
|
8
|
+
const hasClusterCorestoreKeys = (valueArg, keysArg, optionalKeysArg = []) => {
|
|
9
|
+
const actualKeys = Object.keys(valueArg);
|
|
10
|
+
return keysArg.every((keyArg) => actualKeys.includes(keyArg))
|
|
11
|
+
&& actualKeys.every((keyArg) => keysArg.includes(keyArg) || optionalKeysArg.includes(keyArg));
|
|
12
|
+
};
|
|
13
|
+
const validateClusterCorestoreServiceResources = (valueArg, pathArg) => {
|
|
14
|
+
if (!isClusterCorestoreRecord(valueArg)
|
|
15
|
+
|| !hasClusterCorestoreKeys(valueArg, ['serviceId', 'resources', 'updatedAt'], ['serviceName'])) {
|
|
16
|
+
return [`${pathArg} must use its exact schema`];
|
|
17
|
+
}
|
|
18
|
+
const errors = [];
|
|
19
|
+
if (typeof valueArg.serviceId !== 'string'
|
|
20
|
+
|| !clusterCorestoreInventoryIdentifierPattern.test(valueArg.serviceId)) {
|
|
21
|
+
errors.push(`${pathArg} serviceId must be a bounded identifier`);
|
|
22
|
+
}
|
|
23
|
+
if (!Number.isSafeInteger(valueArg.updatedAt) || valueArg.updatedAt < 0) {
|
|
24
|
+
errors.push(`${pathArg} updatedAt must be a non-negative safe integer`);
|
|
25
|
+
}
|
|
26
|
+
if (!Array.isArray(valueArg.resources)) {
|
|
27
|
+
errors.push(`${pathArg} resources must be an array`);
|
|
28
|
+
return errors;
|
|
29
|
+
}
|
|
30
|
+
for (const [index, resource] of valueArg.resources.entries()) {
|
|
31
|
+
const resourcePath = `${pathArg} resources[${index}]`;
|
|
32
|
+
if (!isClusterCorestoreRecord(resource)
|
|
33
|
+
|| !hasClusterCorestoreKeys(resource, ['capability', 'provider', 'resourceName', 'createdAt'])) {
|
|
34
|
+
errors.push(`${resourcePath} must use its exact schema`);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (resource.capability !== 'database' && resource.capability !== 'objectstorage') {
|
|
38
|
+
errors.push(`${resourcePath} capability must be database or objectstorage`);
|
|
39
|
+
}
|
|
40
|
+
if (typeof resource.provider !== 'string' || resource.provider.length === 0) {
|
|
41
|
+
errors.push(`${resourcePath} provider must be a non-empty string`);
|
|
42
|
+
}
|
|
43
|
+
if (typeof resource.resourceName !== 'string' || resource.resourceName.length === 0) {
|
|
44
|
+
errors.push(`${resourcePath} resourceName must be a non-empty string`);
|
|
45
|
+
}
|
|
46
|
+
if (!Number.isSafeInteger(resource.createdAt) || resource.createdAt < 0) {
|
|
47
|
+
errors.push(`${resourcePath} createdAt must be a non-negative safe integer`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return errors;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Validates one cluster Corestore inventory and names every reason it is
|
|
54
|
+
* refused.
|
|
55
|
+
*
|
|
56
|
+
* Shape only. Whether that node belongs to the cluster, and whether a resource
|
|
57
|
+
* should exist, are decisions for the control plane that asked; an inventory
|
|
58
|
+
* that passes here has claimed neither. A reachable inventory that names an
|
|
59
|
+
* error, and an unreachable one that lists resources, are both refused: an
|
|
60
|
+
* answer has to be one of the two things it can be.
|
|
61
|
+
*/
|
|
62
|
+
export const validateClusterCorestoreInventory = (valueArg, pathArg = 'cluster corestore inventory') => {
|
|
63
|
+
if (!isClusterCorestoreRecord(valueArg)
|
|
64
|
+
|| !hasClusterCorestoreKeys(valueArg, ['nodeId', 'checkedAt', 'reachable', 'services'], ['errorCode'])) {
|
|
65
|
+
return [`${pathArg} must use its exact schema`];
|
|
66
|
+
}
|
|
67
|
+
const errors = [];
|
|
68
|
+
if (typeof valueArg.nodeId !== 'string'
|
|
69
|
+
|| !clusterCorestoreInventoryIdentifierPattern.test(valueArg.nodeId)) {
|
|
70
|
+
errors.push(`${pathArg} nodeId must be a bounded identifier`);
|
|
71
|
+
}
|
|
72
|
+
if (!Number.isSafeInteger(valueArg.checkedAt) || valueArg.checkedAt < 0) {
|
|
73
|
+
errors.push(`${pathArg} checkedAt must be a non-negative safe integer`);
|
|
74
|
+
}
|
|
75
|
+
if (typeof valueArg.reachable !== 'boolean') {
|
|
76
|
+
errors.push(`${pathArg} reachable must be a boolean`);
|
|
77
|
+
}
|
|
78
|
+
if (valueArg.errorCode !== undefined
|
|
79
|
+
&& valueArg.errorCode !== 'CORESTORE_ENDPOINT_UNKNOWN'
|
|
80
|
+
&& valueArg.errorCode !== 'CORESTORE_UNREACHABLE') {
|
|
81
|
+
errors.push(`${pathArg} errorCode must name why the node could not be read`);
|
|
82
|
+
}
|
|
83
|
+
if (valueArg.reachable === true && valueArg.errorCode !== undefined) {
|
|
84
|
+
errors.push(`${pathArg} must not carry an errorCode while it reports the node reachable`);
|
|
85
|
+
}
|
|
86
|
+
if (valueArg.reachable === false && valueArg.errorCode === undefined) {
|
|
87
|
+
// An unreachable node with no reason reads like a node holding nothing,
|
|
88
|
+
// which is the one confusion this shape exists to prevent.
|
|
89
|
+
errors.push(`${pathArg} must name why the node could not be read while it reports it unreachable`);
|
|
90
|
+
}
|
|
91
|
+
if (!Array.isArray(valueArg.services)) {
|
|
92
|
+
errors.push(`${pathArg} services must be an array`);
|
|
93
|
+
return errors;
|
|
94
|
+
}
|
|
95
|
+
if (valueArg.reachable === false && valueArg.services.length > 0) {
|
|
96
|
+
errors.push(`${pathArg} must list no services while it reports the node unreachable`);
|
|
97
|
+
}
|
|
98
|
+
for (const [index, service] of valueArg.services.entries()) {
|
|
99
|
+
errors.push(...validateClusterCorestoreServiceResources(service, `${pathArg} services[${index}]`));
|
|
100
|
+
}
|
|
101
|
+
return errors;
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29yZXN0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvcmVxdWVzdHMvY29yZXN0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBNEd6QyxNQUFNLDBDQUEwQyxHQUFHLHFDQUFxQyxDQUFDO0FBRXpGLE1BQU0sd0JBQXdCLEdBQUcsQ0FDL0IsUUFBaUIsRUFDb0IsRUFBRSxDQUFDLENBQ3hDLE9BQU8sQ0FBQyxRQUFRLENBQUM7T0FDZCxPQUFPLFFBQVEsS0FBSyxRQUFRO09BQzVCLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUM7T0FDeEIsQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLFFBQVEsQ0FBQyxLQUFLLE1BQU0sQ0FBQyxTQUFTO1dBQ25ELE1BQU0sQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLEtBQUssSUFBSSxDQUFDLENBQy9DLENBQUM7QUFFRixNQUFNLHVCQUF1QixHQUFHLENBQzlCLFFBQWlDLEVBQ2pDLE9BQTBCLEVBQzFCLGtCQUFxQyxFQUFFLEVBQzlCLEVBQUU7SUFDWCxNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3pDLE9BQU8sT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQztXQUN4RCxVQUFVLENBQUMsS0FBSyxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLGVBQWUsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztBQUNsRyxDQUFDLENBQUM7QUFFRixNQUFNLHdDQUF3QyxHQUFHLENBQy9DLFFBQWlCLEVBQ2pCLE9BQWUsRUFDTCxFQUFFO0lBQ1osSUFBSSxDQUFDLHdCQUF3QixDQUFDLFFBQVEsQ0FBQztXQUNsQyxDQUFDLHVCQUF1QixDQUN6QixRQUFRLEVBQ1IsQ0FBQyxXQUFXLEVBQUUsV0FBVyxFQUFFLFdBQVcsQ0FBQyxFQUN2QyxDQUFDLGFBQWEsQ0FBQyxDQUNoQixFQUFFLENBQUM7UUFDSixPQUFPLENBQUMsR0FBRyxPQUFPLDRCQUE0QixDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUNELE1BQU0sTUFBTSxHQUFhLEVBQUUsQ0FBQztJQUM1QixJQUFJLE9BQU8sUUFBUSxDQUFDLFNBQVMsS0FBSyxRQUFRO1dBQ3JDLENBQUMsMENBQTBDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDO1FBQzFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxPQUFPLHlDQUF5QyxDQUFDLENBQUM7SUFDbkUsQ0FBQztJQUNELElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsSUFBSyxRQUFRLENBQUMsU0FBb0IsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNwRixNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsT0FBTyxnREFBZ0QsQ0FBQyxDQUFDO0lBQzFFLENBQUM7SUFDRCxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztRQUN2QyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsT0FBTyw2QkFBNkIsQ0FBQyxDQUFDO1FBQ3JELE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFDRCxLQUFLLE1BQU0sQ0FBQyxLQUFLLEVBQUUsUUFBUSxDQUFDLElBQUksUUFBUSxDQUFDLFNBQVMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDO1FBQzdELE1BQU0sWUFBWSxHQUFHLEdBQUcsT0FBTyxjQUFjLEtBQUssR0FBRyxDQUFDO1FBQ3RELElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxRQUFRLENBQUM7ZUFDbEMsQ0FBQyx1QkFBdUIsQ0FDekIsUUFBUSxFQUNSLENBQUMsWUFBWSxFQUFFLFVBQVUsRUFBRSxjQUFjLEVBQUUsV0FBVyxDQUFDLENBQ3hELEVBQUUsQ0FBQztZQUNKLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxZQUFZLDRCQUE0QixDQUFDLENBQUM7WUFDekQsU0FBUztRQUNYLENBQUM7UUFDRCxJQUFJLFFBQVEsQ0FBQyxVQUFVLEtBQUssVUFBVSxJQUFJLFFBQVEsQ0FBQyxVQUFVLEtBQUssZUFBZSxFQUFFLENBQUM7WUFDbEYsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLFlBQVksK0NBQStDLENBQUMsQ0FBQztRQUM5RSxDQUFDO1FBQ0QsSUFBSSxPQUFPLFFBQVEsQ0FBQyxRQUFRLEtBQUssUUFBUSxJQUFJLFFBQVEsQ0FBQyxRQUFRLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzVFLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxZQUFZLHNDQUFzQyxDQUFDLENBQUM7UUFDckUsQ0FBQztRQUNELElBQUksT0FBTyxRQUFRLENBQUMsWUFBWSxLQUFLLFFBQVEsSUFBSSxRQUFRLENBQUMsWUFBWSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUNwRixNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsWUFBWSwwQ0FBMEMsQ0FBQyxDQUFDO1FBQ3pFLENBQUM7UUFDRCxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLElBQUssUUFBUSxDQUFDLFNBQW9CLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDcEYsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLFlBQVksZ0RBQWdELENBQUMsQ0FBQztRQUMvRSxDQUFDO0lBQ0gsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUMsQ0FBQztBQUVGOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sQ0FBQyxNQUFNLGlDQUFpQyxHQUFHLENBQy9DLFFBQWlCLEVBQ2pCLE9BQU8sR0FBRyw2QkFBNkIsRUFDN0IsRUFBRTtJQUNaLElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxRQUFRLENBQUM7V0FDbEMsQ0FBQyx1QkFBdUIsQ0FDekIsUUFBUSxFQUNSLENBQUMsUUFBUSxFQUFFLFdBQVcsRUFBRSxXQUFXLEVBQUUsVUFBVSxDQUFDLEVBQ2hELENBQUMsV0FBVyxDQUFDLENBQ2QsRUFBRSxDQUFDO1FBQ0osT0FBTyxDQUFDLEdBQUcsT0FBTyw0QkFBNEIsQ0FBQyxDQUFDO0lBQ2xELENBQUM7SUFDRCxNQUFNLE1BQU0sR0FBYSxFQUFFLENBQUM7SUFDNUIsSUFBSSxPQUFPLFFBQVEsQ0FBQyxNQUFNLEtBQUssUUFBUTtXQUNsQyxDQUFDLDBDQUEwQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztRQUN2RSxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsT0FBTyxzQ0FBc0MsQ0FBQyxDQUFDO0lBQ2hFLENBQUM7SUFDRCxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLElBQUssUUFBUSxDQUFDLFNBQW9CLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDcEYsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLE9BQU8sZ0RBQWdELENBQUMsQ0FBQztJQUMxRSxDQUFDO0lBQ0QsSUFBSSxPQUFPLFFBQVEsQ0FBQyxTQUFTLEtBQUssU0FBUyxFQUFFLENBQUM7UUFDNUMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLE9BQU8sOEJBQThCLENBQUMsQ0FBQztJQUN4RCxDQUFDO0lBQ0QsSUFBSSxRQUFRLENBQUMsU0FBUyxLQUFLLFNBQVM7V0FDL0IsUUFBUSxDQUFDLFNBQVMsS0FBSyw0QkFBNEI7V0FDbkQsUUFBUSxDQUFDLFNBQVMsS0FBSyx1QkFBdUIsRUFBRSxDQUFDO1FBQ3BELE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxPQUFPLHFEQUFxRCxDQUFDLENBQUM7SUFDL0UsQ0FBQztJQUNELElBQUksUUFBUSxDQUFDLFNBQVMsS0FBSyxJQUFJLElBQUksUUFBUSxDQUFDLFNBQVMsS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUNwRSxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsT0FBTyxrRUFBa0UsQ0FBQyxDQUFDO0lBQzVGLENBQUM7SUFDRCxJQUFJLFFBQVEsQ0FBQyxTQUFTLEtBQUssS0FBSyxJQUFJLFFBQVEsQ0FBQyxTQUFTLEtBQUssU0FBUyxFQUFFLENBQUM7UUFDckUsd0VBQXdFO1FBQ3hFLDJEQUEyRDtRQUMzRCxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsT0FBTywyRUFBMkUsQ0FBQyxDQUFDO0lBQ3JHLENBQUM7SUFDRCxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUN0QyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsT0FBTyw0QkFBNEIsQ0FBQyxDQUFDO1FBQ3BELE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFDRCxJQUFJLFFBQVEsQ0FBQyxTQUFTLEtBQUssS0FBSyxJQUFJLFFBQVEsQ0FBQyxRQUFRLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ2pFLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxPQUFPLDhEQUE4RCxDQUFDLENBQUM7SUFDeEYsQ0FBQztJQUNELEtBQUssTUFBTSxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsSUFBSSxRQUFRLENBQUMsUUFBUSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUM7UUFDM0QsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLHdDQUF3QyxDQUFDLE9BQU8sRUFBRSxHQUFHLE9BQU8sYUFBYSxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDckcsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUMsQ0FBQyJ9
|
|
@@ -150,6 +150,37 @@ export interface IReq_Any_Cloudly_DeleteNodeById extends plugins.typedrequestInt
|
|
|
150
150
|
ok: boolean;
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* An administrator states where one node's Corestore control API answers.
|
|
155
|
+
*
|
|
156
|
+
* Corestore is node-local, so one relay serving a whole cluster must be told per node where to
|
|
157
|
+
* dial; the cluster-local alias every component used while Docker Swarm resolved it is exactly
|
|
158
|
+
* what stops resolving. The endpoint is control-plane state Cloudly owns and the relay reads,
|
|
159
|
+
* so an administrator sets it here rather than a relay discovering it.
|
|
160
|
+
*
|
|
161
|
+
* Cloudly validates the endpoint with `validateClusterNodeCorestore` before it writes, takes the
|
|
162
|
+
* node's own real-write fence inside the writing transaction so a concurrent deletion conflicts
|
|
163
|
+
* instead of leaving an endpoint behind for a node that is gone, and pushes the cluster
|
|
164
|
+
* configuration afterwards so every connected relay learns the change without reconnecting.
|
|
165
|
+
*
|
|
166
|
+
* `endpoint: null` clears it, which is the same fact as never having set one: the node runs no
|
|
167
|
+
* Corestore the relay may reach, and an inventory for it answers
|
|
168
|
+
* `errorCode: 'CORESTORE_ENDPOINT_UNKNOWN'` rather than guessing a hostname. The endpoint says
|
|
169
|
+
* where, never with what — no credential travels in this request or in the node document.
|
|
170
|
+
*/
|
|
171
|
+
export interface IReq_Any_Cloudly_SetClusterNodeCorestoreEndpoint extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_SetClusterNodeCorestoreEndpoint> {
|
|
172
|
+
method: 'setClusterNodeCorestoreEndpoint';
|
|
173
|
+
request: {
|
|
174
|
+
identity: IIdentity;
|
|
175
|
+
nodeId: string;
|
|
176
|
+
/** A canonical `http:` or `https:` origin, or `null` to state that this node has none. */
|
|
177
|
+
endpoint: string | null;
|
|
178
|
+
};
|
|
179
|
+
response: {
|
|
180
|
+
/** The node as it now stands, so a caller needs no second read to see what it set. */
|
|
181
|
+
node: IClusterNode;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
153
184
|
export interface IRequest_Spark_Cloudly_SendHeartbeat extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IRequest_Spark_Cloudly_SendHeartbeat> {
|
|
154
185
|
method: 'sparkNodeHeartbeat';
|
|
155
186
|
request: {
|
|
@@ -12,6 +12,36 @@ export interface IReq_Any_Cloudly_GetPlatformDesiredState extends plugins.typedr
|
|
|
12
12
|
bindings: IPlatformBinding[];
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* One platform binding and the node its Corestore resources live on.
|
|
17
|
+
*
|
|
18
|
+
* Corestore is node-local, so a binding without a node is a database nobody can
|
|
19
|
+
* place: while every cluster ran one coreflow per node, "this node" was whoever
|
|
20
|
+
* answered, and a cluster relay serving every node has no such implicit answer.
|
|
21
|
+
* The placement is Cloudly's, derived from where the service is assigned.
|
|
22
|
+
*/
|
|
23
|
+
export interface IClusterPlatformBindingPlacement {
|
|
24
|
+
nodeId: string;
|
|
25
|
+
binding: IPlatformBinding;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The desired platform state for the relay's own cluster, node by node.
|
|
29
|
+
*
|
|
30
|
+
* Its session decides which cluster it speaks for, so the request carries no
|
|
31
|
+
* cluster of its own. It replaces `getPlatformDesiredState` for a cluster
|
|
32
|
+
* relay: the same capabilities and provider configs, and bindings that state
|
|
33
|
+
* where they belong instead of leaving it to be inferred.
|
|
34
|
+
*/
|
|
35
|
+
export interface IReq_Relay_Cloudly_GetClusterPlatformDesiredState extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Relay_Cloudly_GetClusterPlatformDesiredState> {
|
|
36
|
+
method: 'getClusterPlatformDesiredState';
|
|
37
|
+
request: Record<string, never>;
|
|
38
|
+
response: {
|
|
39
|
+
capabilities: IPlatformCapability[];
|
|
40
|
+
providerConfigs: IPlatformProviderConfig[];
|
|
41
|
+
/** Sorted by node, then by binding id, so two reads compare directly. */
|
|
42
|
+
bindings: IClusterPlatformBindingPlacement[];
|
|
43
|
+
};
|
|
44
|
+
}
|
|
15
45
|
export interface IReq_Any_Cloudly_GetPlatformCapabilities extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_GetPlatformCapabilities> {
|
|
16
46
|
method: 'getPlatformCapabilities';
|
|
17
47
|
request: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
|
-
import type { IActiveSecretRecipientMetadata, IIdentityCredential, ISecretVersionReference } from './data/index.js';
|
|
2
|
+
import type { IActiveSecretRecipientMetadata, ICorestoreDatabaseAllocationReference, IIdentityCredential, ISecretVersionReference } from './data/index.js';
|
|
3
3
|
import type { ISecretEnvelopeAdmissionBindingV1 } from './runtime.js';
|
|
4
4
|
import type { IObjectStorageRetentionIntentV1, IObjectStorageRetentionEvidenceExpectationV1, IObjectStorageRetentionEvidenceV1 } from './platform/objectstorageretention.js';
|
|
5
5
|
export type TCorestoreCredentialCapability = 'database' | 'objectstorage';
|
|
@@ -257,14 +257,12 @@ export declare const corestoreDatabaseBackupRuntimeLimits: Readonly<{
|
|
|
257
257
|
maximumClosureChunks: 1000000;
|
|
258
258
|
maximumServiceNameBytes: 1024;
|
|
259
259
|
}>;
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
receiptSha256: string;
|
|
267
|
-
}
|
|
260
|
+
/**
|
|
261
|
+
* Declared in the data tree, because an isolated restore's canonical resource
|
|
262
|
+
* mappings bind the exact same reference and that tree carries no runtime
|
|
263
|
+
* import. Re-exported here so this module's readers keep one import site.
|
|
264
|
+
*/
|
|
265
|
+
export type { ICorestoreDatabaseAllocationReference };
|
|
268
266
|
export interface ICorestoreDatabaseBackupSnapshotTags {
|
|
269
267
|
corestore: 'resource';
|
|
270
268
|
serviceId: string;
|
|
@@ -336,4 +334,3 @@ export declare const computeCorestoreDatabaseAllocationReferenceSha256: (valueAr
|
|
|
336
334
|
export declare const computeCorestoreDatabaseBackupReceiptSha256: (valueArg: unknown) => Promise<string>;
|
|
337
335
|
export declare const computeCorestoreDatabaseBackupRestoreRequestSha256: (valueArg: unknown) => Promise<string>;
|
|
338
336
|
export declare const computeCorestoreDatabaseBackupRestoreResponseSha256: (valueArg: unknown) => Promise<string>;
|
|
339
|
-
export {};
|