@serve.zone/interfaces 10.0.0 → 11.0.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 +20 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/backup.d.ts +229 -0
- package/dist_ts/requests/backup.d.ts +153 -2
- package/package.json +3 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/backup.ts +308 -0
- package/ts/requests/backup.ts +195 -1
package/changelog.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-07-13 - 11.0.0
|
|
4
|
+
|
|
5
|
+
### Breaking Changes
|
|
6
|
+
|
|
7
|
+
- require JWT-only credentials for public isolated restore requests (backup)
|
|
8
|
+
- Replace caller-visible identity claims with `IIdentityCredential` on create, list, get, and cleanup requests.
|
|
9
|
+
- Require Cloudly handlers to verify the JWT and derive actor, role, and tenant authority server-side.
|
|
10
|
+
|
|
11
|
+
## 2026-07-13 - 10.1.0
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
- define isolated restore orchestration contracts (backup)
|
|
16
|
+
- Add non-runnable scratch namespaces with backup-derived cluster and source authority.
|
|
17
|
+
- Add idempotency keys, revision compare-and-swap, exact cluster/node routing, verification, cursor pagination, durable backup holds, and retryable per-resource cleanup progress.
|
|
18
|
+
|
|
19
|
+
### Maintenance
|
|
20
|
+
|
|
21
|
+
- Remove the legacy implicit-any build suppression and refresh the age-eligible TypeScript toolchain.
|
|
22
|
+
|
|
3
23
|
## 2026-07-10 - 10.0.0
|
|
4
24
|
|
|
5
25
|
### Breaking Changes
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/interfaces',
|
|
6
|
-
version: '
|
|
6
|
+
version: '11.0.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=
|
package/dist_ts/data/backup.d.ts
CHANGED
|
@@ -53,6 +53,19 @@ export interface IBackupRestoreEvent {
|
|
|
53
53
|
status: 'restored' | 'failed';
|
|
54
54
|
errorText?: string;
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Durable retention hold owned by one isolated restore. An entry is active
|
|
58
|
+
* while `releasedAt` is absent. Active holds never expire implicitly:
|
|
59
|
+
* retention may neither claim nor delete the backup until every active hold
|
|
60
|
+
* has been released after successful scratch-resource and staging cleanup.
|
|
61
|
+
*/
|
|
62
|
+
export interface IBackupRestoreHold {
|
|
63
|
+
restoreId: string;
|
|
64
|
+
idempotencyKey: string;
|
|
65
|
+
acquiredAt: number;
|
|
66
|
+
releasedAt?: number;
|
|
67
|
+
releaseReason?: string;
|
|
68
|
+
}
|
|
56
69
|
export interface IBackupRecord {
|
|
57
70
|
id: string;
|
|
58
71
|
serviceId: string;
|
|
@@ -76,6 +89,12 @@ export interface IBackupRecord {
|
|
|
76
89
|
requestedBy?: string;
|
|
77
90
|
errorText?: string;
|
|
78
91
|
restoreHistory?: IBackupRestoreEvent[];
|
|
92
|
+
/**
|
|
93
|
+
* Authoritative isolated-restore holds. Cloudly atomically appends the
|
|
94
|
+
* active entry with restore creation. Failed archive cleanup must retain
|
|
95
|
+
* this backup record so hold recovery remains possible.
|
|
96
|
+
*/
|
|
97
|
+
restoreHolds?: IBackupRestoreHold[];
|
|
79
98
|
tags?: Record<string, string>;
|
|
80
99
|
}
|
|
81
100
|
/** Retention policy understood by containerarchive prune. */
|
|
@@ -92,3 +111,213 @@ export interface IArchivePruneResult {
|
|
|
92
111
|
freedBytes: number;
|
|
93
112
|
dryRun: boolean;
|
|
94
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* JWT-free actor projection persisted with isolated-restore audit data.
|
|
116
|
+
* Authentication credentials must never be copied into restore records.
|
|
117
|
+
*/
|
|
118
|
+
export interface IIsolatedRestoreActor {
|
|
119
|
+
userId: string;
|
|
120
|
+
type: 'human' | 'machine' | 'system';
|
|
121
|
+
role?: 'admin' | 'user' | 'api' | 'cluster';
|
|
122
|
+
}
|
|
123
|
+
export type TIsolatedRestoreStatus = 'pending' | 'preparing' | 'staging' | 'restoring' | 'verifying' | 'ready' | 'failed' | 'cleanup-pending' | 'cleaning' | 'cleaned' | 'cleanup-failed';
|
|
124
|
+
export type TIsolatedRestoreErrorPhase = 'validation' | 'source-backup-hold' | 'preparation' | 'staging' | 'restoring' | 'verifying' | 'cleanup';
|
|
125
|
+
/** Append-only structured failure entry for one isolated restore. */
|
|
126
|
+
export interface IIsolatedRestoreError {
|
|
127
|
+
sequence: number;
|
|
128
|
+
phase: TIsolatedRestoreErrorPhase;
|
|
129
|
+
code: string;
|
|
130
|
+
message: string;
|
|
131
|
+
retryable: boolean;
|
|
132
|
+
occurredAt: number;
|
|
133
|
+
}
|
|
134
|
+
export type TIsolatedRestoreAuditAction = 'created' | 'status-transitioned' | 'source-backup-hold-acquired' | 'cleanup-requested' | 'expired' | 'source-backup-hold-release-requested' | 'source-backup-hold-released';
|
|
135
|
+
/** Append-only lifecycle event. `sequence` is strictly increasing per restore. */
|
|
136
|
+
export interface IIsolatedRestoreAuditEvent {
|
|
137
|
+
sequence: number;
|
|
138
|
+
action: TIsolatedRestoreAuditAction;
|
|
139
|
+
actor: IIsolatedRestoreActor;
|
|
140
|
+
occurredAt: number;
|
|
141
|
+
fromStatus?: TIsolatedRestoreStatus;
|
|
142
|
+
toStatus?: TIsolatedRestoreStatus;
|
|
143
|
+
reason?: string;
|
|
144
|
+
}
|
|
145
|
+
export interface IIsolatedRestoreVolumeMapping {
|
|
146
|
+
/** Stable server-generated id used by verification and cleanup progress. */
|
|
147
|
+
id: string;
|
|
148
|
+
type: 'volume';
|
|
149
|
+
source: {
|
|
150
|
+
snapshotId: string;
|
|
151
|
+
volumeName: string;
|
|
152
|
+
mountPath: string;
|
|
153
|
+
};
|
|
154
|
+
target: {
|
|
155
|
+
volumeName: string;
|
|
156
|
+
mountPath: string;
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
export interface IIsolatedRestoreDatabaseMapping {
|
|
160
|
+
/** Stable server-generated id used by verification and cleanup progress. */
|
|
161
|
+
id: string;
|
|
162
|
+
type: 'database';
|
|
163
|
+
source: {
|
|
164
|
+
snapshotId: string;
|
|
165
|
+
resourceName: string;
|
|
166
|
+
databaseName: string;
|
|
167
|
+
};
|
|
168
|
+
target: {
|
|
169
|
+
resourceName: string;
|
|
170
|
+
databaseName: string;
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
export interface IIsolatedRestoreObjectStorageMapping {
|
|
174
|
+
/** Stable server-generated id used by verification and cleanup progress. */
|
|
175
|
+
id: string;
|
|
176
|
+
type: 'objectstorage';
|
|
177
|
+
source: {
|
|
178
|
+
snapshotId: string;
|
|
179
|
+
resourceName: string;
|
|
180
|
+
bucketName: string;
|
|
181
|
+
};
|
|
182
|
+
target: {
|
|
183
|
+
resourceName: string;
|
|
184
|
+
bucketName: string;
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
export type TIsolatedRestoreResourceMapping = IIsolatedRestoreVolumeMapping | IIsolatedRestoreDatabaseMapping | IIsolatedRestoreObjectStorageMapping;
|
|
188
|
+
export type TIsolatedRestoreVerificationStatus = 'pending' | 'running' | 'passed' | 'failed';
|
|
189
|
+
export interface IIsolatedRestoreVerificationCheck {
|
|
190
|
+
/** Stable machine-readable check code. */
|
|
191
|
+
code: string;
|
|
192
|
+
status: 'passed' | 'failed';
|
|
193
|
+
message?: string;
|
|
194
|
+
}
|
|
195
|
+
export interface IIsolatedRestoreResourceVerification {
|
|
196
|
+
resourceMappingId: string;
|
|
197
|
+
resourceType: TBackupResourceType;
|
|
198
|
+
status: TIsolatedRestoreVerificationStatus;
|
|
199
|
+
checks: IIsolatedRestoreVerificationCheck[];
|
|
200
|
+
checkedAt?: number;
|
|
201
|
+
}
|
|
202
|
+
/** Complete verification state returned by the target data plane. */
|
|
203
|
+
export interface IIsolatedRestoreVerification {
|
|
204
|
+
status: TIsolatedRestoreVerificationStatus;
|
|
205
|
+
results: IIsolatedRestoreResourceVerification[];
|
|
206
|
+
startedAt?: number;
|
|
207
|
+
completedAt?: number;
|
|
208
|
+
}
|
|
209
|
+
export type TIsolatedRestoreCleanupTrigger = 'manual' | 'expired' | 'restore-failed' | 'policy';
|
|
210
|
+
export type TIsolatedRestoreCleanupProgressStatus = 'pending' | 'running' | 'succeeded' | 'failed';
|
|
211
|
+
export interface IIsolatedRestoreResourceCleanupProgress {
|
|
212
|
+
resourceMappingId: string;
|
|
213
|
+
resourceType: TBackupResourceType;
|
|
214
|
+
status: TIsolatedRestoreCleanupProgressStatus;
|
|
215
|
+
attempts: number;
|
|
216
|
+
updatedAt?: number;
|
|
217
|
+
errorCode?: string;
|
|
218
|
+
errorMessage?: string;
|
|
219
|
+
}
|
|
220
|
+
export interface IIsolatedRestoreStagingCleanupProgress {
|
|
221
|
+
stagingArchiveId: string;
|
|
222
|
+
status: TIsolatedRestoreCleanupProgressStatus;
|
|
223
|
+
attempts: number;
|
|
224
|
+
updatedAt?: number;
|
|
225
|
+
errorCode?: string;
|
|
226
|
+
errorMessage?: string;
|
|
227
|
+
}
|
|
228
|
+
/** Data-plane cleanup progress shared by public and internal status views. */
|
|
229
|
+
export interface IIsolatedRestoreCleanupProgress {
|
|
230
|
+
resources: IIsolatedRestoreResourceCleanupProgress[];
|
|
231
|
+
stagingArchive: IIsolatedRestoreStagingCleanupProgress;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Cleanup metadata. The source-backup hold remains active until every
|
|
235
|
+
* resource entry and the staging entry report `succeeded`. `cleanup-failed`
|
|
236
|
+
* is retryable and must not release the hold.
|
|
237
|
+
*/
|
|
238
|
+
export interface IIsolatedRestoreCleanup extends IIsolatedRestoreCleanupProgress {
|
|
239
|
+
trigger: TIsolatedRestoreCleanupTrigger;
|
|
240
|
+
reason: string;
|
|
241
|
+
requestedBy: IIsolatedRestoreActor;
|
|
242
|
+
requestedAt: number;
|
|
243
|
+
startedAt?: number;
|
|
244
|
+
completedAt?: number;
|
|
245
|
+
}
|
|
246
|
+
export type TIsolatedRestoreSourceBackupHoldStatus = 'acquired' | 'release-pending' | 'released';
|
|
247
|
+
/**
|
|
248
|
+
* Restore-side projection of the authoritative IBackupRecord hold. The hold
|
|
249
|
+
* is non-expiring and may enter `released` only after exact cleanup success.
|
|
250
|
+
*/
|
|
251
|
+
export interface IIsolatedRestoreSourceBackupHold {
|
|
252
|
+
status: TIsolatedRestoreSourceBackupHoldStatus;
|
|
253
|
+
acquiredAt: number;
|
|
254
|
+
releaseRequestedAt?: number;
|
|
255
|
+
releasedAt?: number;
|
|
256
|
+
releaseReason?: string;
|
|
257
|
+
}
|
|
258
|
+
export type TIsolatedRestoreNodeStatus = 'preparing' | 'prepared' | 'staging' | 'restoring' | 'verifying' | 'ready' | 'failed' | 'cleaning' | 'cleaned' | 'cleanup-failed';
|
|
259
|
+
/**
|
|
260
|
+
* Data-plane state for one exact cluster/node/restore routing key. `revision`
|
|
261
|
+
* advances after every mutation and is the compare-and-swap value used by
|
|
262
|
+
* prepare, execute, and cleanup requests.
|
|
263
|
+
*/
|
|
264
|
+
export interface IIsolatedRestoreNodeState {
|
|
265
|
+
restoreId: string;
|
|
266
|
+
clusterId: string;
|
|
267
|
+
targetNodeName: string;
|
|
268
|
+
sourceBackupId: string;
|
|
269
|
+
sourceServiceId: string;
|
|
270
|
+
scratchNamespaceId: string;
|
|
271
|
+
stagingArchiveId: string;
|
|
272
|
+
revision: number;
|
|
273
|
+
status: TIsolatedRestoreNodeStatus;
|
|
274
|
+
completedResourceMappingIds: string[];
|
|
275
|
+
verification: IIsolatedRestoreVerification;
|
|
276
|
+
cleanup?: IIsolatedRestoreCleanupProgress;
|
|
277
|
+
preparedAt?: number;
|
|
278
|
+
readyAt?: number;
|
|
279
|
+
updatedAt: number;
|
|
280
|
+
lastErrorCode?: string;
|
|
281
|
+
lastErrorMessage?: string;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Durable control-plane record for a restore into a generated data-plane
|
|
285
|
+
* scratch namespace. The namespace is not an IService and carries no image,
|
|
286
|
+
* runtime, route, port, environment, or secret authority.
|
|
287
|
+
*
|
|
288
|
+
* `clusterId` and `sourceServiceId` are copied from the authoritative source
|
|
289
|
+
* backup, never accepted from the caller. `revision` advances monotonically
|
|
290
|
+
* for optimistic lifecycle updates. A restore execution failure (`failed`)
|
|
291
|
+
* must proceed through cleanup before its source-backup hold can be released.
|
|
292
|
+
*/
|
|
293
|
+
export interface IIsolatedRestoreRecord {
|
|
294
|
+
/** Server-generated restore id. */
|
|
295
|
+
id: string;
|
|
296
|
+
/** Caller key used to return the same restore after retried creation. */
|
|
297
|
+
idempotencyKey: string;
|
|
298
|
+
revision: number;
|
|
299
|
+
sourceBackupId: string;
|
|
300
|
+
sourceServiceId: string;
|
|
301
|
+
sourceServiceName?: string;
|
|
302
|
+
clusterId: string;
|
|
303
|
+
/** Server-generated, non-runnable data-plane namespace. */
|
|
304
|
+
scratchNamespaceId: string;
|
|
305
|
+
/** Server-generated archive namespace, distinct from sourceBackupId. */
|
|
306
|
+
stagingArchiveId: string;
|
|
307
|
+
targetNodeName: string;
|
|
308
|
+
purpose: string;
|
|
309
|
+
requestedBy: IIsolatedRestoreActor;
|
|
310
|
+
status: TIsolatedRestoreStatus;
|
|
311
|
+
resourceMappings: TIsolatedRestoreResourceMapping[];
|
|
312
|
+
verification: IIsolatedRestoreVerification;
|
|
313
|
+
sourceBackupHold: IIsolatedRestoreSourceBackupHold;
|
|
314
|
+
createdAt: number;
|
|
315
|
+
updatedAt: number;
|
|
316
|
+
expiresAt: number;
|
|
317
|
+
readyAt?: number;
|
|
318
|
+
cleanup?: IIsolatedRestoreCleanup;
|
|
319
|
+
/** Append-only; entries are never replaced when a later phase fails. */
|
|
320
|
+
errors: IIsolatedRestoreError[];
|
|
321
|
+
/** Append-only lifecycle audit trail. */
|
|
322
|
+
auditEvents: IIsolatedRestoreAuditEvent[];
|
|
323
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
|
-
import type { IArchivePruneResult, IArchiveRetentionPolicy, IBackupArchiveManifest, IBackupArchiveObject, IBackupRecord, IBackupReplicationResult, TBackupSnapshot } from '../data/backup.js';
|
|
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
|
-
import type { IIdentity } from '../data/user.js';
|
|
4
|
+
import type { IIdentity, IIdentityCredential } from '../data/user.js';
|
|
5
5
|
export interface IReq_Any_Cloudly_CreateServiceBackup extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_CreateServiceBackup> {
|
|
6
6
|
method: 'createServiceBackup';
|
|
7
7
|
request: {
|
|
@@ -146,3 +146,154 @@ export interface IReq_Cloudly_Coreflow_PruneNodeArchive extends plugins.typedreq
|
|
|
146
146
|
result?: IArchivePruneResult;
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Creates a restore in a new non-runnable data-plane scratch namespace.
|
|
151
|
+
* Cloudly derives clusterId and sourceServiceId from the authoritative backup,
|
|
152
|
+
* atomically acquires its retention hold, and owns every generated target id.
|
|
153
|
+
*/
|
|
154
|
+
export interface IReq_Any_Cloudly_CreateIsolatedRestore extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_CreateIsolatedRestore> {
|
|
155
|
+
method: 'createIsolatedRestore';
|
|
156
|
+
request: {
|
|
157
|
+
/** Cloudly verifies this credential and derives all actor claims server-side. */
|
|
158
|
+
identity: IIdentityCredential;
|
|
159
|
+
sourceBackupId: string;
|
|
160
|
+
targetNodeName: string;
|
|
161
|
+
purpose: string;
|
|
162
|
+
/** Caller-generated key; retries return the original restore. */
|
|
163
|
+
idempotencyKey: string;
|
|
164
|
+
/** Requested lifetime; Cloudly applies its configured maximum. */
|
|
165
|
+
ttlMs?: number;
|
|
166
|
+
/** Derived from the authoritative backup. */
|
|
167
|
+
clusterId?: never;
|
|
168
|
+
/** Derived from the authoritative backup. */
|
|
169
|
+
sourceServiceId?: never;
|
|
170
|
+
/** Generated by Cloudly. Explicitly forbidden as caller input. */
|
|
171
|
+
id?: never;
|
|
172
|
+
/** Alias-like caller input is forbidden as well. */
|
|
173
|
+
restoreId?: never;
|
|
174
|
+
/** Legacy runnable-service targeting is forbidden. */
|
|
175
|
+
targetServiceId?: never;
|
|
176
|
+
/** Generated by Cloudly. Explicitly forbidden as caller input. */
|
|
177
|
+
scratchNamespaceId?: never;
|
|
178
|
+
/** Generated by Cloudly. Explicitly forbidden as caller input. */
|
|
179
|
+
stagingArchiveId?: never;
|
|
180
|
+
/** Generated after validating every snapshot in the source backup. */
|
|
181
|
+
resourceMappings?: never;
|
|
182
|
+
};
|
|
183
|
+
response: {
|
|
184
|
+
restore: IIsolatedRestoreRecord;
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
export interface IReq_Any_Cloudly_GetIsolatedRestores extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_GetIsolatedRestores> {
|
|
188
|
+
method: 'getIsolatedRestores';
|
|
189
|
+
request: {
|
|
190
|
+
/** Cloudly verifies this credential and derives all actor claims server-side. */
|
|
191
|
+
identity: IIdentityCredential;
|
|
192
|
+
sourceBackupId?: string;
|
|
193
|
+
sourceServiceId?: string;
|
|
194
|
+
clusterId?: string;
|
|
195
|
+
targetNodeName?: string;
|
|
196
|
+
scratchNamespaceId?: string;
|
|
197
|
+
status?: IIsolatedRestoreRecord['status'];
|
|
198
|
+
includeCleaned?: boolean;
|
|
199
|
+
/** Opaque cursor returned by the previous page. */
|
|
200
|
+
cursor?: string;
|
|
201
|
+
/** Server-capped page size. */
|
|
202
|
+
limit?: number;
|
|
203
|
+
};
|
|
204
|
+
response: {
|
|
205
|
+
restores: IIsolatedRestoreRecord[];
|
|
206
|
+
/** Omitted when this is the final page. */
|
|
207
|
+
nextCursor?: string;
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
export interface IReq_Any_Cloudly_GetIsolatedRestoreById extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_GetIsolatedRestoreById> {
|
|
211
|
+
method: 'getIsolatedRestoreById';
|
|
212
|
+
request: {
|
|
213
|
+
/** Cloudly verifies this credential and derives all actor claims server-side. */
|
|
214
|
+
identity: IIdentityCredential;
|
|
215
|
+
restoreId: string;
|
|
216
|
+
};
|
|
217
|
+
response: {
|
|
218
|
+
restore: IIsolatedRestoreRecord;
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Prepares and stages one isolated restore on an exact cluster node. A missing
|
|
223
|
+
* node state has revision 0; successful mutation returns the next revision.
|
|
224
|
+
*/
|
|
225
|
+
export interface IReq_Cloudly_Coreflow_PrepareIsolatedRestore extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Coreflow_PrepareIsolatedRestore> {
|
|
226
|
+
method: 'coreflowPrepareIsolatedRestore';
|
|
227
|
+
request: {
|
|
228
|
+
clusterId: string;
|
|
229
|
+
targetNodeName: string;
|
|
230
|
+
restoreId: string;
|
|
231
|
+
expectedRevision: number;
|
|
232
|
+
sourceBackupId: string;
|
|
233
|
+
sourceServiceId: string;
|
|
234
|
+
scratchNamespaceId: string;
|
|
235
|
+
stagingArchiveId: string;
|
|
236
|
+
resourceMappings: TIsolatedRestoreResourceMapping[];
|
|
237
|
+
archiveManifest: IBackupArchiveManifest;
|
|
238
|
+
};
|
|
239
|
+
response: {
|
|
240
|
+
state: IIsolatedRestoreNodeState;
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/** Read-only status for one exact cluster/node/restore routing key. */
|
|
244
|
+
export interface IReq_Cloudly_Coreflow_GetIsolatedRestoreStatus extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Coreflow_GetIsolatedRestoreStatus> {
|
|
245
|
+
method: 'coreflowGetIsolatedRestoreStatus';
|
|
246
|
+
request: {
|
|
247
|
+
clusterId: string;
|
|
248
|
+
targetNodeName: string;
|
|
249
|
+
restoreId: string;
|
|
250
|
+
};
|
|
251
|
+
response: {
|
|
252
|
+
found: boolean;
|
|
253
|
+
state?: IIsolatedRestoreNodeState;
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
export interface IReq_Cloudly_Coreflow_ExecuteIsolatedRestore extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Coreflow_ExecuteIsolatedRestore> {
|
|
257
|
+
method: 'coreflowExecuteIsolatedRestore';
|
|
258
|
+
request: {
|
|
259
|
+
clusterId: string;
|
|
260
|
+
targetNodeName: string;
|
|
261
|
+
restoreId: string;
|
|
262
|
+
expectedRevision: number;
|
|
263
|
+
};
|
|
264
|
+
response: {
|
|
265
|
+
state: IIsolatedRestoreNodeState;
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Idempotent cleanup mutation. A cleanup-failed state remains retryable and
|
|
270
|
+
* does not authorize release of the source-backup hold.
|
|
271
|
+
*/
|
|
272
|
+
export interface IReq_Cloudly_Coreflow_CleanupIsolatedRestore extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_Coreflow_CleanupIsolatedRestore> {
|
|
273
|
+
method: 'coreflowCleanupIsolatedRestore';
|
|
274
|
+
request: {
|
|
275
|
+
clusterId: string;
|
|
276
|
+
targetNodeName: string;
|
|
277
|
+
restoreId: string;
|
|
278
|
+
expectedRevision: number;
|
|
279
|
+
};
|
|
280
|
+
response: {
|
|
281
|
+
state: IIsolatedRestoreNodeState;
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Requests idempotent asynchronous cleanup. The restore record is retained as
|
|
286
|
+
* audit evidence and transitions through the canonical cleanup statuses.
|
|
287
|
+
*/
|
|
288
|
+
export interface IReq_Any_Cloudly_CleanupIsolatedRestore extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_CleanupIsolatedRestore> {
|
|
289
|
+
method: 'cleanupIsolatedRestore';
|
|
290
|
+
request: {
|
|
291
|
+
/** Cloudly verifies this credential and derives all actor claims server-side. */
|
|
292
|
+
identity: IIdentityCredential;
|
|
293
|
+
restoreId: string;
|
|
294
|
+
reason: string;
|
|
295
|
+
};
|
|
296
|
+
response: {
|
|
297
|
+
restore: IIsolatedRestoreRecord;
|
|
298
|
+
};
|
|
299
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serve.zone/interfaces",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.",
|
|
6
6
|
"exports": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"@git.zone/tsdoc": "^2.1.1",
|
|
20
20
|
"@git.zone/tsrun": "^2.0.5",
|
|
21
21
|
"@git.zone/tstest": "^3.6.6",
|
|
22
|
-
"@types/node": "^26.0
|
|
22
|
+
"@types/node": "^26.1.0"
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
"ts/**/*",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
],
|
|
53
53
|
"scripts": {
|
|
54
54
|
"test": "tstest test/ --verbose --logfile --timeout 60",
|
|
55
|
-
"build": "tsbuild tsfolders
|
|
55
|
+
"build": "tsbuild tsfolders",
|
|
56
56
|
"buildDocs": "tsdoc"
|
|
57
57
|
}
|
|
58
58
|
}
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/data/backup.ts
CHANGED
|
@@ -77,6 +77,20 @@ export interface IBackupRestoreEvent {
|
|
|
77
77
|
errorText?: string;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Durable retention hold owned by one isolated restore. An entry is active
|
|
82
|
+
* while `releasedAt` is absent. Active holds never expire implicitly:
|
|
83
|
+
* retention may neither claim nor delete the backup until every active hold
|
|
84
|
+
* has been released after successful scratch-resource and staging cleanup.
|
|
85
|
+
*/
|
|
86
|
+
export interface IBackupRestoreHold {
|
|
87
|
+
restoreId: string;
|
|
88
|
+
idempotencyKey: string;
|
|
89
|
+
acquiredAt: number;
|
|
90
|
+
releasedAt?: number;
|
|
91
|
+
releaseReason?: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
80
94
|
export interface IBackupRecord {
|
|
81
95
|
id: string;
|
|
82
96
|
serviceId: string;
|
|
@@ -100,6 +114,12 @@ export interface IBackupRecord {
|
|
|
100
114
|
requestedBy?: string;
|
|
101
115
|
errorText?: string;
|
|
102
116
|
restoreHistory?: IBackupRestoreEvent[];
|
|
117
|
+
/**
|
|
118
|
+
* Authoritative isolated-restore holds. Cloudly atomically appends the
|
|
119
|
+
* active entry with restore creation. Failed archive cleanup must retain
|
|
120
|
+
* this backup record so hold recovery remains possible.
|
|
121
|
+
*/
|
|
122
|
+
restoreHolds?: IBackupRestoreHold[];
|
|
103
123
|
tags?: Record<string, string>;
|
|
104
124
|
}
|
|
105
125
|
|
|
@@ -118,3 +138,291 @@ export interface IArchivePruneResult {
|
|
|
118
138
|
freedBytes: number;
|
|
119
139
|
dryRun: boolean;
|
|
120
140
|
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* JWT-free actor projection persisted with isolated-restore audit data.
|
|
144
|
+
* Authentication credentials must never be copied into restore records.
|
|
145
|
+
*/
|
|
146
|
+
export interface IIsolatedRestoreActor {
|
|
147
|
+
userId: string;
|
|
148
|
+
type: 'human' | 'machine' | 'system';
|
|
149
|
+
role?: 'admin' | 'user' | 'api' | 'cluster';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export type TIsolatedRestoreStatus =
|
|
153
|
+
| 'pending'
|
|
154
|
+
| 'preparing'
|
|
155
|
+
| 'staging'
|
|
156
|
+
| 'restoring'
|
|
157
|
+
| 'verifying'
|
|
158
|
+
| 'ready'
|
|
159
|
+
| 'failed'
|
|
160
|
+
| 'cleanup-pending'
|
|
161
|
+
| 'cleaning'
|
|
162
|
+
| 'cleaned'
|
|
163
|
+
| 'cleanup-failed';
|
|
164
|
+
|
|
165
|
+
export type TIsolatedRestoreErrorPhase =
|
|
166
|
+
| 'validation'
|
|
167
|
+
| 'source-backup-hold'
|
|
168
|
+
| 'preparation'
|
|
169
|
+
| 'staging'
|
|
170
|
+
| 'restoring'
|
|
171
|
+
| 'verifying'
|
|
172
|
+
| 'cleanup';
|
|
173
|
+
|
|
174
|
+
/** Append-only structured failure entry for one isolated restore. */
|
|
175
|
+
export interface IIsolatedRestoreError {
|
|
176
|
+
sequence: number;
|
|
177
|
+
phase: TIsolatedRestoreErrorPhase;
|
|
178
|
+
code: string;
|
|
179
|
+
message: string;
|
|
180
|
+
retryable: boolean;
|
|
181
|
+
occurredAt: number;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export type TIsolatedRestoreAuditAction =
|
|
185
|
+
| 'created'
|
|
186
|
+
| 'status-transitioned'
|
|
187
|
+
| 'source-backup-hold-acquired'
|
|
188
|
+
| 'cleanup-requested'
|
|
189
|
+
| 'expired'
|
|
190
|
+
| 'source-backup-hold-release-requested'
|
|
191
|
+
| 'source-backup-hold-released';
|
|
192
|
+
|
|
193
|
+
/** Append-only lifecycle event. `sequence` is strictly increasing per restore. */
|
|
194
|
+
export interface IIsolatedRestoreAuditEvent {
|
|
195
|
+
sequence: number;
|
|
196
|
+
action: TIsolatedRestoreAuditAction;
|
|
197
|
+
actor: IIsolatedRestoreActor;
|
|
198
|
+
occurredAt: number;
|
|
199
|
+
fromStatus?: TIsolatedRestoreStatus;
|
|
200
|
+
toStatus?: TIsolatedRestoreStatus;
|
|
201
|
+
reason?: string;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export interface IIsolatedRestoreVolumeMapping {
|
|
205
|
+
/** Stable server-generated id used by verification and cleanup progress. */
|
|
206
|
+
id: string;
|
|
207
|
+
type: 'volume';
|
|
208
|
+
source: {
|
|
209
|
+
snapshotId: string;
|
|
210
|
+
volumeName: string;
|
|
211
|
+
mountPath: string;
|
|
212
|
+
};
|
|
213
|
+
target: {
|
|
214
|
+
volumeName: string;
|
|
215
|
+
mountPath: string;
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export interface IIsolatedRestoreDatabaseMapping {
|
|
220
|
+
/** Stable server-generated id used by verification and cleanup progress. */
|
|
221
|
+
id: string;
|
|
222
|
+
type: 'database';
|
|
223
|
+
source: {
|
|
224
|
+
snapshotId: string;
|
|
225
|
+
resourceName: string;
|
|
226
|
+
databaseName: string;
|
|
227
|
+
};
|
|
228
|
+
target: {
|
|
229
|
+
resourceName: string;
|
|
230
|
+
databaseName: string;
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export interface IIsolatedRestoreObjectStorageMapping {
|
|
235
|
+
/** Stable server-generated id used by verification and cleanup progress. */
|
|
236
|
+
id: string;
|
|
237
|
+
type: 'objectstorage';
|
|
238
|
+
source: {
|
|
239
|
+
snapshotId: string;
|
|
240
|
+
resourceName: string;
|
|
241
|
+
bucketName: string;
|
|
242
|
+
};
|
|
243
|
+
target: {
|
|
244
|
+
resourceName: string;
|
|
245
|
+
bucketName: string;
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export type TIsolatedRestoreResourceMapping =
|
|
250
|
+
| IIsolatedRestoreVolumeMapping
|
|
251
|
+
| IIsolatedRestoreDatabaseMapping
|
|
252
|
+
| IIsolatedRestoreObjectStorageMapping;
|
|
253
|
+
|
|
254
|
+
export type TIsolatedRestoreVerificationStatus =
|
|
255
|
+
| 'pending'
|
|
256
|
+
| 'running'
|
|
257
|
+
| 'passed'
|
|
258
|
+
| 'failed';
|
|
259
|
+
|
|
260
|
+
export interface IIsolatedRestoreVerificationCheck {
|
|
261
|
+
/** Stable machine-readable check code. */
|
|
262
|
+
code: string;
|
|
263
|
+
status: 'passed' | 'failed';
|
|
264
|
+
message?: string;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export interface IIsolatedRestoreResourceVerification {
|
|
268
|
+
resourceMappingId: string;
|
|
269
|
+
resourceType: TBackupResourceType;
|
|
270
|
+
status: TIsolatedRestoreVerificationStatus;
|
|
271
|
+
checks: IIsolatedRestoreVerificationCheck[];
|
|
272
|
+
checkedAt?: number;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Complete verification state returned by the target data plane. */
|
|
276
|
+
export interface IIsolatedRestoreVerification {
|
|
277
|
+
status: TIsolatedRestoreVerificationStatus;
|
|
278
|
+
results: IIsolatedRestoreResourceVerification[];
|
|
279
|
+
startedAt?: number;
|
|
280
|
+
completedAt?: number;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export type TIsolatedRestoreCleanupTrigger =
|
|
284
|
+
| 'manual'
|
|
285
|
+
| 'expired'
|
|
286
|
+
| 'restore-failed'
|
|
287
|
+
| 'policy';
|
|
288
|
+
|
|
289
|
+
export type TIsolatedRestoreCleanupProgressStatus =
|
|
290
|
+
| 'pending'
|
|
291
|
+
| 'running'
|
|
292
|
+
| 'succeeded'
|
|
293
|
+
| 'failed';
|
|
294
|
+
|
|
295
|
+
export interface IIsolatedRestoreResourceCleanupProgress {
|
|
296
|
+
resourceMappingId: string;
|
|
297
|
+
resourceType: TBackupResourceType;
|
|
298
|
+
status: TIsolatedRestoreCleanupProgressStatus;
|
|
299
|
+
attempts: number;
|
|
300
|
+
updatedAt?: number;
|
|
301
|
+
errorCode?: string;
|
|
302
|
+
errorMessage?: string;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export interface IIsolatedRestoreStagingCleanupProgress {
|
|
306
|
+
stagingArchiveId: string;
|
|
307
|
+
status: TIsolatedRestoreCleanupProgressStatus;
|
|
308
|
+
attempts: number;
|
|
309
|
+
updatedAt?: number;
|
|
310
|
+
errorCode?: string;
|
|
311
|
+
errorMessage?: string;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Data-plane cleanup progress shared by public and internal status views. */
|
|
315
|
+
export interface IIsolatedRestoreCleanupProgress {
|
|
316
|
+
resources: IIsolatedRestoreResourceCleanupProgress[];
|
|
317
|
+
stagingArchive: IIsolatedRestoreStagingCleanupProgress;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Cleanup metadata. The source-backup hold remains active until every
|
|
322
|
+
* resource entry and the staging entry report `succeeded`. `cleanup-failed`
|
|
323
|
+
* is retryable and must not release the hold.
|
|
324
|
+
*/
|
|
325
|
+
export interface IIsolatedRestoreCleanup extends IIsolatedRestoreCleanupProgress {
|
|
326
|
+
trigger: TIsolatedRestoreCleanupTrigger;
|
|
327
|
+
reason: string;
|
|
328
|
+
requestedBy: IIsolatedRestoreActor;
|
|
329
|
+
requestedAt: number;
|
|
330
|
+
startedAt?: number;
|
|
331
|
+
completedAt?: number;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export type TIsolatedRestoreSourceBackupHoldStatus =
|
|
335
|
+
| 'acquired'
|
|
336
|
+
| 'release-pending'
|
|
337
|
+
| 'released';
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Restore-side projection of the authoritative IBackupRecord hold. The hold
|
|
341
|
+
* is non-expiring and may enter `released` only after exact cleanup success.
|
|
342
|
+
*/
|
|
343
|
+
export interface IIsolatedRestoreSourceBackupHold {
|
|
344
|
+
status: TIsolatedRestoreSourceBackupHoldStatus;
|
|
345
|
+
acquiredAt: number;
|
|
346
|
+
releaseRequestedAt?: number;
|
|
347
|
+
releasedAt?: number;
|
|
348
|
+
releaseReason?: string;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export type TIsolatedRestoreNodeStatus =
|
|
352
|
+
| 'preparing'
|
|
353
|
+
| 'prepared'
|
|
354
|
+
| 'staging'
|
|
355
|
+
| 'restoring'
|
|
356
|
+
| 'verifying'
|
|
357
|
+
| 'ready'
|
|
358
|
+
| 'failed'
|
|
359
|
+
| 'cleaning'
|
|
360
|
+
| 'cleaned'
|
|
361
|
+
| 'cleanup-failed';
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Data-plane state for one exact cluster/node/restore routing key. `revision`
|
|
365
|
+
* advances after every mutation and is the compare-and-swap value used by
|
|
366
|
+
* prepare, execute, and cleanup requests.
|
|
367
|
+
*/
|
|
368
|
+
export interface IIsolatedRestoreNodeState {
|
|
369
|
+
restoreId: string;
|
|
370
|
+
clusterId: string;
|
|
371
|
+
targetNodeName: string;
|
|
372
|
+
sourceBackupId: string;
|
|
373
|
+
sourceServiceId: string;
|
|
374
|
+
scratchNamespaceId: string;
|
|
375
|
+
stagingArchiveId: string;
|
|
376
|
+
revision: number;
|
|
377
|
+
status: TIsolatedRestoreNodeStatus;
|
|
378
|
+
completedResourceMappingIds: string[];
|
|
379
|
+
verification: IIsolatedRestoreVerification;
|
|
380
|
+
cleanup?: IIsolatedRestoreCleanupProgress;
|
|
381
|
+
preparedAt?: number;
|
|
382
|
+
readyAt?: number;
|
|
383
|
+
updatedAt: number;
|
|
384
|
+
lastErrorCode?: string;
|
|
385
|
+
lastErrorMessage?: string;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Durable control-plane record for a restore into a generated data-plane
|
|
390
|
+
* scratch namespace. The namespace is not an IService and carries no image,
|
|
391
|
+
* runtime, route, port, environment, or secret authority.
|
|
392
|
+
*
|
|
393
|
+
* `clusterId` and `sourceServiceId` are copied from the authoritative source
|
|
394
|
+
* backup, never accepted from the caller. `revision` advances monotonically
|
|
395
|
+
* for optimistic lifecycle updates. A restore execution failure (`failed`)
|
|
396
|
+
* must proceed through cleanup before its source-backup hold can be released.
|
|
397
|
+
*/
|
|
398
|
+
export interface IIsolatedRestoreRecord {
|
|
399
|
+
/** Server-generated restore id. */
|
|
400
|
+
id: string;
|
|
401
|
+
/** Caller key used to return the same restore after retried creation. */
|
|
402
|
+
idempotencyKey: string;
|
|
403
|
+
revision: number;
|
|
404
|
+
sourceBackupId: string;
|
|
405
|
+
sourceServiceId: string;
|
|
406
|
+
sourceServiceName?: string;
|
|
407
|
+
clusterId: string;
|
|
408
|
+
/** Server-generated, non-runnable data-plane namespace. */
|
|
409
|
+
scratchNamespaceId: string;
|
|
410
|
+
/** Server-generated archive namespace, distinct from sourceBackupId. */
|
|
411
|
+
stagingArchiveId: string;
|
|
412
|
+
targetNodeName: string;
|
|
413
|
+
purpose: string;
|
|
414
|
+
requestedBy: IIsolatedRestoreActor;
|
|
415
|
+
status: TIsolatedRestoreStatus;
|
|
416
|
+
resourceMappings: TIsolatedRestoreResourceMapping[];
|
|
417
|
+
verification: IIsolatedRestoreVerification;
|
|
418
|
+
sourceBackupHold: IIsolatedRestoreSourceBackupHold;
|
|
419
|
+
createdAt: number;
|
|
420
|
+
updatedAt: number;
|
|
421
|
+
expiresAt: number;
|
|
422
|
+
readyAt?: number;
|
|
423
|
+
cleanup?: IIsolatedRestoreCleanup;
|
|
424
|
+
/** Append-only; entries are never replaced when a later phase fails. */
|
|
425
|
+
errors: IIsolatedRestoreError[];
|
|
426
|
+
/** Append-only lifecycle audit trail. */
|
|
427
|
+
auditEvents: IIsolatedRestoreAuditEvent[];
|
|
428
|
+
}
|
package/ts/requests/backup.ts
CHANGED
|
@@ -6,10 +6,13 @@ import type {
|
|
|
6
6
|
IBackupArchiveObject,
|
|
7
7
|
IBackupRecord,
|
|
8
8
|
IBackupReplicationResult,
|
|
9
|
+
IIsolatedRestoreNodeState,
|
|
10
|
+
IIsolatedRestoreRecord,
|
|
9
11
|
TBackupSnapshot,
|
|
12
|
+
TIsolatedRestoreResourceMapping,
|
|
10
13
|
} from '../data/backup.js';
|
|
11
14
|
import type { IService } from '../data/service.js';
|
|
12
|
-
import type { IIdentity } from '../data/user.js';
|
|
15
|
+
import type { IIdentity, IIdentityCredential } from '../data/user.js';
|
|
13
16
|
|
|
14
17
|
export interface IReq_Any_Cloudly_CreateServiceBackup
|
|
15
18
|
extends plugins.typedrequestInterfaces.implementsTR<
|
|
@@ -214,3 +217,194 @@ extends plugins.typedrequestInterfaces.implementsTR<
|
|
|
214
217
|
result?: IArchivePruneResult;
|
|
215
218
|
};
|
|
216
219
|
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Creates a restore in a new non-runnable data-plane scratch namespace.
|
|
223
|
+
* Cloudly derives clusterId and sourceServiceId from the authoritative backup,
|
|
224
|
+
* atomically acquires its retention hold, and owns every generated target id.
|
|
225
|
+
*/
|
|
226
|
+
export interface IReq_Any_Cloudly_CreateIsolatedRestore
|
|
227
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
228
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
229
|
+
IReq_Any_Cloudly_CreateIsolatedRestore
|
|
230
|
+
> {
|
|
231
|
+
method: 'createIsolatedRestore';
|
|
232
|
+
request: {
|
|
233
|
+
/** Cloudly verifies this credential and derives all actor claims server-side. */
|
|
234
|
+
identity: IIdentityCredential;
|
|
235
|
+
sourceBackupId: string;
|
|
236
|
+
targetNodeName: string;
|
|
237
|
+
purpose: string;
|
|
238
|
+
/** Caller-generated key; retries return the original restore. */
|
|
239
|
+
idempotencyKey: string;
|
|
240
|
+
/** Requested lifetime; Cloudly applies its configured maximum. */
|
|
241
|
+
ttlMs?: number;
|
|
242
|
+
/** Derived from the authoritative backup. */
|
|
243
|
+
clusterId?: never;
|
|
244
|
+
/** Derived from the authoritative backup. */
|
|
245
|
+
sourceServiceId?: never;
|
|
246
|
+
/** Generated by Cloudly. Explicitly forbidden as caller input. */
|
|
247
|
+
id?: never;
|
|
248
|
+
/** Alias-like caller input is forbidden as well. */
|
|
249
|
+
restoreId?: never;
|
|
250
|
+
/** Legacy runnable-service targeting is forbidden. */
|
|
251
|
+
targetServiceId?: never;
|
|
252
|
+
/** Generated by Cloudly. Explicitly forbidden as caller input. */
|
|
253
|
+
scratchNamespaceId?: never;
|
|
254
|
+
/** Generated by Cloudly. Explicitly forbidden as caller input. */
|
|
255
|
+
stagingArchiveId?: never;
|
|
256
|
+
/** Generated after validating every snapshot in the source backup. */
|
|
257
|
+
resourceMappings?: never;
|
|
258
|
+
};
|
|
259
|
+
response: {
|
|
260
|
+
restore: IIsolatedRestoreRecord;
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface IReq_Any_Cloudly_GetIsolatedRestores
|
|
265
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
266
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
267
|
+
IReq_Any_Cloudly_GetIsolatedRestores
|
|
268
|
+
> {
|
|
269
|
+
method: 'getIsolatedRestores';
|
|
270
|
+
request: {
|
|
271
|
+
/** Cloudly verifies this credential and derives all actor claims server-side. */
|
|
272
|
+
identity: IIdentityCredential;
|
|
273
|
+
sourceBackupId?: string;
|
|
274
|
+
sourceServiceId?: string;
|
|
275
|
+
clusterId?: string;
|
|
276
|
+
targetNodeName?: string;
|
|
277
|
+
scratchNamespaceId?: string;
|
|
278
|
+
status?: IIsolatedRestoreRecord['status'];
|
|
279
|
+
includeCleaned?: boolean;
|
|
280
|
+
/** Opaque cursor returned by the previous page. */
|
|
281
|
+
cursor?: string;
|
|
282
|
+
/** Server-capped page size. */
|
|
283
|
+
limit?: number;
|
|
284
|
+
};
|
|
285
|
+
response: {
|
|
286
|
+
restores: IIsolatedRestoreRecord[];
|
|
287
|
+
/** Omitted when this is the final page. */
|
|
288
|
+
nextCursor?: string;
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export interface IReq_Any_Cloudly_GetIsolatedRestoreById
|
|
293
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
294
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
295
|
+
IReq_Any_Cloudly_GetIsolatedRestoreById
|
|
296
|
+
> {
|
|
297
|
+
method: 'getIsolatedRestoreById';
|
|
298
|
+
request: {
|
|
299
|
+
/** Cloudly verifies this credential and derives all actor claims server-side. */
|
|
300
|
+
identity: IIdentityCredential;
|
|
301
|
+
restoreId: string;
|
|
302
|
+
};
|
|
303
|
+
response: {
|
|
304
|
+
restore: IIsolatedRestoreRecord;
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Prepares and stages one isolated restore on an exact cluster node. A missing
|
|
310
|
+
* node state has revision 0; successful mutation returns the next revision.
|
|
311
|
+
*/
|
|
312
|
+
export interface IReq_Cloudly_Coreflow_PrepareIsolatedRestore
|
|
313
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
314
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
315
|
+
IReq_Cloudly_Coreflow_PrepareIsolatedRestore
|
|
316
|
+
> {
|
|
317
|
+
method: 'coreflowPrepareIsolatedRestore';
|
|
318
|
+
request: {
|
|
319
|
+
clusterId: string;
|
|
320
|
+
targetNodeName: string;
|
|
321
|
+
restoreId: string;
|
|
322
|
+
expectedRevision: number;
|
|
323
|
+
sourceBackupId: string;
|
|
324
|
+
sourceServiceId: string;
|
|
325
|
+
scratchNamespaceId: string;
|
|
326
|
+
stagingArchiveId: string;
|
|
327
|
+
resourceMappings: TIsolatedRestoreResourceMapping[];
|
|
328
|
+
archiveManifest: IBackupArchiveManifest;
|
|
329
|
+
};
|
|
330
|
+
response: {
|
|
331
|
+
state: IIsolatedRestoreNodeState;
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Read-only status for one exact cluster/node/restore routing key. */
|
|
336
|
+
export interface IReq_Cloudly_Coreflow_GetIsolatedRestoreStatus
|
|
337
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
338
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
339
|
+
IReq_Cloudly_Coreflow_GetIsolatedRestoreStatus
|
|
340
|
+
> {
|
|
341
|
+
method: 'coreflowGetIsolatedRestoreStatus';
|
|
342
|
+
request: {
|
|
343
|
+
clusterId: string;
|
|
344
|
+
targetNodeName: string;
|
|
345
|
+
restoreId: string;
|
|
346
|
+
};
|
|
347
|
+
response: {
|
|
348
|
+
found: boolean;
|
|
349
|
+
state?: IIsolatedRestoreNodeState;
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export interface IReq_Cloudly_Coreflow_ExecuteIsolatedRestore
|
|
354
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
355
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
356
|
+
IReq_Cloudly_Coreflow_ExecuteIsolatedRestore
|
|
357
|
+
> {
|
|
358
|
+
method: 'coreflowExecuteIsolatedRestore';
|
|
359
|
+
request: {
|
|
360
|
+
clusterId: string;
|
|
361
|
+
targetNodeName: string;
|
|
362
|
+
restoreId: string;
|
|
363
|
+
expectedRevision: number;
|
|
364
|
+
};
|
|
365
|
+
response: {
|
|
366
|
+
state: IIsolatedRestoreNodeState;
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Idempotent cleanup mutation. A cleanup-failed state remains retryable and
|
|
372
|
+
* does not authorize release of the source-backup hold.
|
|
373
|
+
*/
|
|
374
|
+
export interface IReq_Cloudly_Coreflow_CleanupIsolatedRestore
|
|
375
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
376
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
377
|
+
IReq_Cloudly_Coreflow_CleanupIsolatedRestore
|
|
378
|
+
> {
|
|
379
|
+
method: 'coreflowCleanupIsolatedRestore';
|
|
380
|
+
request: {
|
|
381
|
+
clusterId: string;
|
|
382
|
+
targetNodeName: string;
|
|
383
|
+
restoreId: string;
|
|
384
|
+
expectedRevision: number;
|
|
385
|
+
};
|
|
386
|
+
response: {
|
|
387
|
+
state: IIsolatedRestoreNodeState;
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Requests idempotent asynchronous cleanup. The restore record is retained as
|
|
393
|
+
* audit evidence and transitions through the canonical cleanup statuses.
|
|
394
|
+
*/
|
|
395
|
+
export interface IReq_Any_Cloudly_CleanupIsolatedRestore
|
|
396
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
397
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
398
|
+
IReq_Any_Cloudly_CleanupIsolatedRestore
|
|
399
|
+
> {
|
|
400
|
+
method: 'cleanupIsolatedRestore';
|
|
401
|
+
request: {
|
|
402
|
+
/** Cloudly verifies this credential and derives all actor claims server-side. */
|
|
403
|
+
identity: IIdentityCredential;
|
|
404
|
+
restoreId: string;
|
|
405
|
+
reason: string;
|
|
406
|
+
};
|
|
407
|
+
response: {
|
|
408
|
+
restore: IIsolatedRestoreRecord;
|
|
409
|
+
};
|
|
410
|
+
}
|