@serve.zone/interfaces 32.35.0 → 32.36.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 +55 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/deploymentlegacyimport.d.ts +92 -0
- package/dist_ts/data/deploymentlegacyimport.js +237 -0
- package/dist_ts/data/deploymentoperation.d.ts +63 -38
- package/dist_ts/data/deploymentoperation.js +12 -2
- package/dist_ts/data/deploymentpreflight.d.ts +42 -10
- package/dist_ts/data/deploymentpreflight.js +47 -10
- package/dist_ts/data/immutableimage.d.ts +26 -2
- package/dist_ts/data/immutableimage.js +12 -2
- package/dist_ts/data/index.d.ts +5 -0
- package/dist_ts/data/index.js +6 -1
- package/dist_ts/data/runtimeassignmentexec.d.ts +110 -0
- package/dist_ts/data/runtimeassignmentexec.js +164 -0
- package/dist_ts/data/runtimeassignmentlogs.d.ts +121 -0
- package/dist_ts/data/runtimeassignmentlogs.js +139 -0
- package/dist_ts/data/runtimeassignmentstats.d.ts +60 -0
- package/dist_ts/data/runtimeassignmentstats.js +93 -0
- package/dist_ts/data/runtimelocalvolumearchive.d.ts +103 -0
- package/dist_ts/data/runtimelocalvolumearchive.js +111 -0
- package/dist_ts/data/user.d.ts +6 -1
- package/dist_ts/data/user.js +6 -1
- package/dist_ts/requests/runtimesession.d.ts +81 -0
- package/dist_ts/requests/service.d.ts +51 -0
- package/package.json +1 -1
- package/readme.md +105 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/deploymentlegacyimport.ts +371 -0
- package/ts/data/deploymentoperation.ts +83 -39
- package/ts/data/deploymentpreflight.ts +86 -18
- package/ts/data/immutableimage.ts +42 -3
- package/ts/data/index.ts +5 -0
- package/ts/data/runtimeassignmentexec.ts +271 -0
- package/ts/data/runtimeassignmentlogs.ts +249 -0
- package/ts/data/runtimeassignmentstats.ts +149 -0
- package/ts/data/runtimelocalvolumearchive.ts +207 -0
- package/ts/data/user.ts +10 -0
- package/ts/requests/runtimesession.ts +102 -0
- package/ts/requests/service.ts +66 -0
package/changelog.md
CHANGED
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-09-29 - 32.36.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- A node's controller can now see and reach its workloads. Four contract families ride on the
|
|
8
|
+
runtime session in `requests.runtimesession`:
|
|
9
|
+
- `reportRuntimeAssignmentLogs`: Pallet pushes batches of an attempt's captured output. Lines
|
|
10
|
+
come in `partial` pieces, and `lost` entries evidence `buffer-overflow`, `line-cut`,
|
|
11
|
+
`capture-restart` and `not-captured` loss. Batches are acknowledged by sequence, so the
|
|
12
|
+
controller persists logs as `IRuntimeConfig.logs` requires.
|
|
13
|
+
- `readRuntimeAssignmentStats`: one CPU, memory, network and writable-layer sample of a
|
|
14
|
+
running attempt.
|
|
15
|
+
- `openRuntimeAssignmentExec`, `resizeRuntimeAssignmentExec` and `closeRuntimeAssignmentExec`:
|
|
16
|
+
a command in a running container, with VirtualStream stdin and output, TTY or not. At most
|
|
17
|
+
four sessions per node, with a 15-minute idle limit.
|
|
18
|
+
- `readRuntimeLocalVolumeArchive` and `writeRuntimeLocalVolumeArchive`: a node-local volume as
|
|
19
|
+
a pax archive. A read refuses a volume a running container holds. A write fills an `import`
|
|
20
|
+
claim that waits in `import-required` and publishes it only after the whole verified archive.
|
|
21
|
+
|
|
22
|
+
Exec and volume archives are refused for any controller but a local one (`onebox`). Each family
|
|
23
|
+
has exact-schema snapshots and request and response binders (`data.runtimeAssignmentLogContract`,
|
|
24
|
+
`runtimeAssignmentStatsContract`, `runtimeAssignmentExecContract`,
|
|
25
|
+
`runtimeLocalVolumeArchiveContract`). Until now a controller had no way to read a Pallet
|
|
26
|
+
workload's output or resources, run a command in it, or back up and restore its volumes, so
|
|
27
|
+
Onebox could not move its services off Docker.
|
|
28
|
+
- `TDeploymentPreflightWarningCode` gains `PLATFORM_BINDING_READINESS_DEFERRED`: the promote
|
|
29
|
+
preflight of a held greenfield service that was never promoted may accept an enabled Corestore
|
|
30
|
+
`database` or `objectstorage` binding that is not `ready` yet, because a held service receives no
|
|
31
|
+
credential material and its binding can only become ready once promotion releases the hold. The
|
|
32
|
+
warning records that acceptance; the route preflight still requires the binding `ready`.
|
|
33
|
+
- A legacy `deployOnPush` service without deployable source can move to a greenfield successor that
|
|
34
|
+
runs the exact image it ran. `data.ILegacyServiceRuntimeAttestation` is an administrator-captured
|
|
35
|
+
record that every required replica ran healthy on one registry root, with that root's platform
|
|
36
|
+
manifests (`ILegacyServiceImageSource`, `ILegacyImagePlatformManifest`);
|
|
37
|
+
`validateLegacyServiceRuntimeAttestation` and `validateLegacyImageImportReference` state the rules.
|
|
38
|
+
A greenfield reservation and its preflight may name `legacyImport` (`ILegacyImageImportReference`)
|
|
39
|
+
in place of `sourceRevision`, `sourceDirty` and release evidence
|
|
40
|
+
(`ILegacyImportServiceDeploymentReservationInput`), and the operation records the same origin.
|
|
41
|
+
New preflight blockers: `LEGACY_IMPORT_ATTESTATION_MISSING`, `LEGACY_IMPORT_SOURCE_MISMATCH` and
|
|
42
|
+
`LEGACY_IMPORT_PLATFORM_MISMATCH`. Release evidence may now be Cloudly's import
|
|
43
|
+
(`IImageReleaseLegacyImportEvidence`, source `cloudly-legacy-import`) beside the registry push
|
|
44
|
+
(`IImageReleaseRegistryPushEvidence`); `selectTrustedImageReleaseEvidence` takes an optional
|
|
45
|
+
`evidenceSource`, which defaults to the registry push. The capability `deployment:import-legacy`
|
|
46
|
+
is valid on organization-service-slot grants only. New requests:
|
|
47
|
+
`captureLegacyServiceRuntimeAttestation` and `getLegacyServiceRuntimeAttestations` (administrators)
|
|
48
|
+
and `importServiceDeploymentLegacyImage`. Until now an immutable deployment needed a source
|
|
49
|
+
revision and a pushed build, so a legacy service whose source was lost could not leave its
|
|
50
|
+
legacy runtime.
|
|
51
|
+
- Type widening for consumers: `IServiceDeploymentOperation.data.sourceRevision` and
|
|
52
|
+
`IDeploymentPreflightReport.source.revision` / `.dirty` are now `string | undefined` /
|
|
53
|
+
`boolean | undefined`, absent exactly when the origin is a legacy import.
|
|
54
|
+
`IServiceDeploymentOperation.data` is the new `IServiceDeploymentOperationData`, and
|
|
55
|
+
`IDeploymentPreflightRequestData` and `IImageReleaseTrustedEvidence` are now union type aliases,
|
|
56
|
+
so they can no longer be extended as interfaces.
|
|
57
|
+
|
|
3
58
|
## 2026-09-29 - 32.35.0
|
|
4
59
|
|
|
5
60
|
### Features
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/interfaces',
|
|
6
|
-
version: '32.
|
|
6
|
+
version: '32.36.0',
|
|
7
7
|
description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFNBQVM7SUFDbEIsV0FBVyxFQUFFLHVGQUF1RjtDQUNyRyxDQUFBIn0=
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { TImmutableContainerPlatform, TOciManifestMediaType, TSha256Digest } from './immutableimage.js';
|
|
2
|
+
/**
|
|
3
|
+
* Legacy image import: a greenfield deployment whose image is the one a legacy `deployOnPush`
|
|
4
|
+
* service already runs, instead of one built from a source revision.
|
|
5
|
+
*
|
|
6
|
+
* A legacy service without deployable source still has an image in Cloudly's registry and, until
|
|
7
|
+
* its runtime is frozen, healthy replicas running it. An administrator captures that fact once as
|
|
8
|
+
* a {@link ILegacyServiceRuntimeAttestation} while the replicas still run. A greenfield successor
|
|
9
|
+
* reservation names the attestation in `legacyImport` instead of a `sourceRevision`, and Cloudly
|
|
10
|
+
* then imports the attested image into the successor's own repository and release tag itself:
|
|
11
|
+
* the bytes never leave the registry and the release records that it was imported, not built.
|
|
12
|
+
*
|
|
13
|
+
* Deployment roots are indexes. An attested root that already is an index is imported as it is. A
|
|
14
|
+
* root that is one platform manifest is imported under an index whose only descriptor is that
|
|
15
|
+
* manifest, in the manifest's own family — a Docker manifest list around a Docker manifest, an OCI
|
|
16
|
+
* index around an OCI manifest — so the platform manifest stays byte-identical and its digest is
|
|
17
|
+
* still the one the legacy runtime ran. The attestation states everything that descriptor needs.
|
|
18
|
+
*/
|
|
19
|
+
/** The media types a legacy image's per-platform manifest can carry. */
|
|
20
|
+
export type TLegacyImagePlatformManifestMediaType = 'application/vnd.oci.image.manifest.v1+json' | 'application/vnd.docker.distribution.manifest.v2+json';
|
|
21
|
+
/** The index media type of the family a platform manifest belongs to. */
|
|
22
|
+
export type TLegacyImageImportIndexMediaType = 'application/vnd.oci.image.index.v1+json' | 'application/vnd.docker.distribution.manifest.list.v2+json';
|
|
23
|
+
/** One platform manifest of a legacy image, as Cloudly read it from its own registry. */
|
|
24
|
+
export interface ILegacyImagePlatformManifest {
|
|
25
|
+
platform: TImmutableContainerPlatform;
|
|
26
|
+
manifestDigest: TSha256Digest;
|
|
27
|
+
manifestMediaType: TLegacyImagePlatformManifestMediaType;
|
|
28
|
+
/** Exact byte length of the platform manifest, which an index descriptor states. */
|
|
29
|
+
manifestSize: number;
|
|
30
|
+
imageConfigDigest: TSha256Digest;
|
|
31
|
+
}
|
|
32
|
+
/** The registry image a legacy service ran, under the mutable tag it was deployed from. */
|
|
33
|
+
export interface ILegacyServiceImageSource {
|
|
34
|
+
registryHost: string;
|
|
35
|
+
repository: string;
|
|
36
|
+
/** The mutable tag the legacy service was deployed from, `latest` included. */
|
|
37
|
+
tag: string;
|
|
38
|
+
/** Digest of the root the tag named: a single platform manifest or an index. */
|
|
39
|
+
rootDigest: TSha256Digest;
|
|
40
|
+
rootMediaType: TOciManifestMediaType;
|
|
41
|
+
/**
|
|
42
|
+
* The runnable Linux platforms the root carries, sorted by platform. A single-manifest root
|
|
43
|
+
* carries exactly one, whose manifest is the root itself.
|
|
44
|
+
*/
|
|
45
|
+
platforms: ILegacyImagePlatformManifest[];
|
|
46
|
+
}
|
|
47
|
+
/** One healthy replica of the legacy service, observed running the attested digest. */
|
|
48
|
+
export interface ILegacyServiceRuntimeTaskAttestation {
|
|
49
|
+
deploymentId: string;
|
|
50
|
+
nodeName: string;
|
|
51
|
+
observedDigest: TSha256Digest;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Durable evidence, captured by an administrator while the legacy runtime still ran, that every
|
|
55
|
+
* required replica of a legacy service was healthy and ran exactly the registry root it names.
|
|
56
|
+
*
|
|
57
|
+
* Cloudly writes one and never changes it. A capture only succeeds on complete evidence, so a
|
|
58
|
+
* stored attestation is by construction one whose runtime digest is its source root digest.
|
|
59
|
+
*/
|
|
60
|
+
export interface ILegacyServiceRuntimeAttestation {
|
|
61
|
+
id: string;
|
|
62
|
+
data: {
|
|
63
|
+
legacyServiceId: string;
|
|
64
|
+
organizationId: string;
|
|
65
|
+
source: ILegacyServiceImageSource;
|
|
66
|
+
runtime: {
|
|
67
|
+
requiredReplicaCount: number;
|
|
68
|
+
healthyReplicaCount: number;
|
|
69
|
+
runtimeDigest: TSha256Digest;
|
|
70
|
+
/** Sorted by deploymentId, one per required replica. */
|
|
71
|
+
tasks: ILegacyServiceRuntimeTaskAttestation[];
|
|
72
|
+
};
|
|
73
|
+
capturedByUserId: string;
|
|
74
|
+
capturedAt: number;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* What a legacy-import reservation names in place of a source revision: the legacy service, the
|
|
79
|
+
* attestation Cloudly holds for it, and the root digest the deployer asserts that attestation
|
|
80
|
+
* carries.
|
|
81
|
+
*/
|
|
82
|
+
export interface ILegacyImageImportReference {
|
|
83
|
+
legacyServiceId: string;
|
|
84
|
+
attestationId: string;
|
|
85
|
+
sourceRootDigest: TSha256Digest;
|
|
86
|
+
}
|
|
87
|
+
/** True when a media type names a single platform manifest rather than an index. */
|
|
88
|
+
export declare const isLegacyImagePlatformManifestMediaType: (valueArg: unknown) => valueArg is TLegacyImagePlatformManifestMediaType;
|
|
89
|
+
/** True when a media type names an index of either family. */
|
|
90
|
+
export declare const isLegacyImageImportIndexMediaType: (valueArg: unknown) => valueArg is TLegacyImageImportIndexMediaType;
|
|
91
|
+
export declare const validateLegacyImageImportReference: (referenceArg: unknown) => string[];
|
|
92
|
+
export declare const validateLegacyServiceRuntimeAttestation: (attestationArg: unknown) => string[];
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { normalizeSha256Digest } from './immutableimage.js';
|
|
2
|
+
import { hasExactKeys, isPlainRecord } from '../private/dataproperties.js';
|
|
3
|
+
const identifierRegex = /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/;
|
|
4
|
+
/** An OCI tag, the mutable `latest` included: the source tag is whatever the legacy service ran. */
|
|
5
|
+
const ociTagRegex = /^[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$/;
|
|
6
|
+
const registryHostRegex = /^[a-z0-9](?:[a-z0-9.-]{0,251}[a-z0-9])?(?::[0-9]{1,5})?$/;
|
|
7
|
+
const repositoryRegex = /^[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*(?:\/[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*)*$/;
|
|
8
|
+
const maximumManifestSize = 4 * 1024 * 1024;
|
|
9
|
+
const maximumReplicaCount = 64;
|
|
10
|
+
const legacyImagePlatformManifestMediaTypes = [
|
|
11
|
+
'application/vnd.oci.image.manifest.v1+json',
|
|
12
|
+
'application/vnd.docker.distribution.manifest.v2+json',
|
|
13
|
+
];
|
|
14
|
+
const legacyImageImportIndexMediaTypes = [
|
|
15
|
+
'application/vnd.oci.image.index.v1+json',
|
|
16
|
+
'application/vnd.docker.distribution.manifest.list.v2+json',
|
|
17
|
+
];
|
|
18
|
+
const legacyImagePlatforms = ['linux/amd64', 'linux/arm64'];
|
|
19
|
+
const isIdentifier = (valueArg) => (typeof valueArg === 'string' && identifierRegex.test(valueArg));
|
|
20
|
+
const isCanonicalDigest = (valueArg) => (typeof valueArg === 'string' && normalizeSha256Digest(valueArg) === valueArg);
|
|
21
|
+
const isPositiveSafeInteger = (valueArg, maximumArg) => (typeof valueArg === 'number'
|
|
22
|
+
&& Number.isSafeInteger(valueArg)
|
|
23
|
+
&& valueArg >= 1
|
|
24
|
+
&& valueArg <= maximumArg);
|
|
25
|
+
/** True when a media type names a single platform manifest rather than an index. */
|
|
26
|
+
export const isLegacyImagePlatformManifestMediaType = (valueArg) => (legacyImagePlatformManifestMediaTypes.some((mediaTypeArg) => mediaTypeArg === valueArg));
|
|
27
|
+
/** True when a media type names an index of either family. */
|
|
28
|
+
export const isLegacyImageImportIndexMediaType = (valueArg) => (legacyImageImportIndexMediaTypes.some((mediaTypeArg) => mediaTypeArg === valueArg));
|
|
29
|
+
export const validateLegacyImageImportReference = (referenceArg) => {
|
|
30
|
+
if (!isPlainRecord(referenceArg))
|
|
31
|
+
return ['legacyImport must be an object'];
|
|
32
|
+
const errors = [];
|
|
33
|
+
if (!hasExactKeys(referenceArg, ['legacyServiceId', 'attestationId', 'sourceRootDigest'])) {
|
|
34
|
+
errors.push('legacyImport must use its exact schema');
|
|
35
|
+
}
|
|
36
|
+
if (!isIdentifier(referenceArg.legacyServiceId)) {
|
|
37
|
+
errors.push('legacyImport.legacyServiceId must be a bounded canonical identifier');
|
|
38
|
+
}
|
|
39
|
+
if (!isIdentifier(referenceArg.attestationId)) {
|
|
40
|
+
errors.push('legacyImport.attestationId must be a bounded canonical identifier');
|
|
41
|
+
}
|
|
42
|
+
if (!isCanonicalDigest(referenceArg.sourceRootDigest)) {
|
|
43
|
+
errors.push('legacyImport.sourceRootDigest must be a canonical sha256 digest');
|
|
44
|
+
}
|
|
45
|
+
return errors;
|
|
46
|
+
};
|
|
47
|
+
const validatePlatformManifest = (manifestArg, indexArg) => {
|
|
48
|
+
const label = `source.platforms[${indexArg}]`;
|
|
49
|
+
if (!isPlainRecord(manifestArg))
|
|
50
|
+
return [`${label} must be an object`];
|
|
51
|
+
const errors = [];
|
|
52
|
+
if (!hasExactKeys(manifestArg, [
|
|
53
|
+
'platform',
|
|
54
|
+
'manifestDigest',
|
|
55
|
+
'manifestMediaType',
|
|
56
|
+
'manifestSize',
|
|
57
|
+
'imageConfigDigest',
|
|
58
|
+
])) {
|
|
59
|
+
errors.push(`${label} must use its exact schema`);
|
|
60
|
+
}
|
|
61
|
+
if (!legacyImagePlatforms.some((platformArg) => platformArg === manifestArg.platform)) {
|
|
62
|
+
errors.push(`${label}.platform must be linux/amd64 or linux/arm64`);
|
|
63
|
+
}
|
|
64
|
+
if (!isCanonicalDigest(manifestArg.manifestDigest)) {
|
|
65
|
+
errors.push(`${label}.manifestDigest must be a canonical sha256 digest`);
|
|
66
|
+
}
|
|
67
|
+
if (!isLegacyImagePlatformManifestMediaType(manifestArg.manifestMediaType)) {
|
|
68
|
+
errors.push(`${label}.manifestMediaType must name an OCI or Docker image manifest`);
|
|
69
|
+
}
|
|
70
|
+
if (!isPositiveSafeInteger(manifestArg.manifestSize, maximumManifestSize)) {
|
|
71
|
+
errors.push(`${label}.manifestSize must be a positive bounded byte length`);
|
|
72
|
+
}
|
|
73
|
+
if (!isCanonicalDigest(manifestArg.imageConfigDigest)) {
|
|
74
|
+
errors.push(`${label}.imageConfigDigest must be a canonical sha256 digest`);
|
|
75
|
+
}
|
|
76
|
+
return errors;
|
|
77
|
+
};
|
|
78
|
+
const validateImageSource = (sourceArg) => {
|
|
79
|
+
if (!isPlainRecord(sourceArg))
|
|
80
|
+
return ['source must be an object'];
|
|
81
|
+
const errors = [];
|
|
82
|
+
if (!hasExactKeys(sourceArg, [
|
|
83
|
+
'registryHost',
|
|
84
|
+
'repository',
|
|
85
|
+
'tag',
|
|
86
|
+
'rootDigest',
|
|
87
|
+
'rootMediaType',
|
|
88
|
+
'platforms',
|
|
89
|
+
])) {
|
|
90
|
+
errors.push('source must use its exact schema');
|
|
91
|
+
}
|
|
92
|
+
if (typeof sourceArg.registryHost !== 'string' || !registryHostRegex.test(sourceArg.registryHost)) {
|
|
93
|
+
errors.push('source.registryHost must be a canonical registry host');
|
|
94
|
+
}
|
|
95
|
+
if (typeof sourceArg.repository !== 'string'
|
|
96
|
+
|| sourceArg.repository.length > 255
|
|
97
|
+
|| !repositoryRegex.test(sourceArg.repository)) {
|
|
98
|
+
errors.push('source.repository must be a canonical OCI repository name');
|
|
99
|
+
}
|
|
100
|
+
if (typeof sourceArg.tag !== 'string' || !ociTagRegex.test(sourceArg.tag)) {
|
|
101
|
+
errors.push('source.tag must be an OCI tag');
|
|
102
|
+
}
|
|
103
|
+
if (!isCanonicalDigest(sourceArg.rootDigest)) {
|
|
104
|
+
errors.push('source.rootDigest must be a canonical sha256 digest');
|
|
105
|
+
}
|
|
106
|
+
const rootIsManifest = isLegacyImagePlatformManifestMediaType(sourceArg.rootMediaType);
|
|
107
|
+
const rootIsIndex = isLegacyImageImportIndexMediaType(sourceArg.rootMediaType);
|
|
108
|
+
if (!rootIsManifest && !rootIsIndex) {
|
|
109
|
+
errors.push('source.rootMediaType must name an OCI or Docker manifest or index');
|
|
110
|
+
}
|
|
111
|
+
if (!Array.isArray(sourceArg.platforms)
|
|
112
|
+
|| sourceArg.platforms.length < 1
|
|
113
|
+
|| sourceArg.platforms.length > legacyImagePlatforms.length) {
|
|
114
|
+
errors.push('source.platforms must carry one or two runnable Linux platforms');
|
|
115
|
+
return errors;
|
|
116
|
+
}
|
|
117
|
+
const platforms = sourceArg.platforms;
|
|
118
|
+
platforms.forEach((manifestArg, indexArg) => {
|
|
119
|
+
errors.push(...validatePlatformManifest(manifestArg, indexArg));
|
|
120
|
+
});
|
|
121
|
+
const platformNames = platforms.map((manifestArg) => (isPlainRecord(manifestArg) ? manifestArg.platform : undefined));
|
|
122
|
+
const sortedPlatformNames = legacyImagePlatforms.filter((platformArg) => (platformNames.includes(platformArg)));
|
|
123
|
+
if (JSON.stringify(platformNames) !== JSON.stringify(sortedPlatformNames)) {
|
|
124
|
+
errors.push('source.platforms must be unique and sorted by platform');
|
|
125
|
+
}
|
|
126
|
+
if (rootIsManifest) {
|
|
127
|
+
const onlyPlatform = platforms[0];
|
|
128
|
+
if (platforms.length !== 1
|
|
129
|
+
|| !isPlainRecord(onlyPlatform)
|
|
130
|
+
|| onlyPlatform.manifestDigest !== sourceArg.rootDigest
|
|
131
|
+
|| onlyPlatform.manifestMediaType !== sourceArg.rootMediaType) {
|
|
132
|
+
errors.push('a single-manifest source root must be its own one platform manifest');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (rootIsIndex && platforms.some((manifestArg) => (isPlainRecord(manifestArg) && manifestArg.manifestDigest === sourceArg.rootDigest))) {
|
|
136
|
+
errors.push('an index source root cannot also be one of its platform manifests');
|
|
137
|
+
}
|
|
138
|
+
return errors;
|
|
139
|
+
};
|
|
140
|
+
const validateRuntime = (runtimeArg, rootDigestArg) => {
|
|
141
|
+
if (!isPlainRecord(runtimeArg))
|
|
142
|
+
return ['runtime must be an object'];
|
|
143
|
+
const errors = [];
|
|
144
|
+
if (!hasExactKeys(runtimeArg, [
|
|
145
|
+
'requiredReplicaCount',
|
|
146
|
+
'healthyReplicaCount',
|
|
147
|
+
'runtimeDigest',
|
|
148
|
+
'tasks',
|
|
149
|
+
])) {
|
|
150
|
+
errors.push('runtime must use its exact schema');
|
|
151
|
+
}
|
|
152
|
+
if (!isPositiveSafeInteger(runtimeArg.requiredReplicaCount, maximumReplicaCount)) {
|
|
153
|
+
errors.push('runtime.requiredReplicaCount must be a positive bounded replica count');
|
|
154
|
+
}
|
|
155
|
+
if (runtimeArg.healthyReplicaCount !== runtimeArg.requiredReplicaCount) {
|
|
156
|
+
errors.push('runtime.healthyReplicaCount must equal the required replica count');
|
|
157
|
+
}
|
|
158
|
+
if (!isCanonicalDigest(runtimeArg.runtimeDigest)) {
|
|
159
|
+
errors.push('runtime.runtimeDigest must be a canonical sha256 digest');
|
|
160
|
+
}
|
|
161
|
+
else if (runtimeArg.runtimeDigest !== rootDigestArg) {
|
|
162
|
+
errors.push('runtime.runtimeDigest must be the attested source root digest');
|
|
163
|
+
}
|
|
164
|
+
if (!Array.isArray(runtimeArg.tasks) || runtimeArg.tasks.length !== runtimeArg.requiredReplicaCount) {
|
|
165
|
+
errors.push('runtime.tasks must hold exactly one task per required replica');
|
|
166
|
+
return errors;
|
|
167
|
+
}
|
|
168
|
+
const tasks = runtimeArg.tasks;
|
|
169
|
+
tasks.forEach((taskArg, indexArg) => {
|
|
170
|
+
const label = `runtime.tasks[${indexArg}]`;
|
|
171
|
+
if (!isPlainRecord(taskArg)) {
|
|
172
|
+
errors.push(`${label} must be an object`);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (!hasExactKeys(taskArg, ['deploymentId', 'nodeName', 'observedDigest'])) {
|
|
176
|
+
errors.push(`${label} must use its exact schema`);
|
|
177
|
+
}
|
|
178
|
+
if (!isIdentifier(taskArg.deploymentId)) {
|
|
179
|
+
errors.push(`${label}.deploymentId must be a bounded canonical identifier`);
|
|
180
|
+
}
|
|
181
|
+
if (typeof taskArg.nodeName !== 'string' || !taskArg.nodeName || taskArg.nodeName.length > 253) {
|
|
182
|
+
errors.push(`${label}.nodeName must be a bounded non-empty name`);
|
|
183
|
+
}
|
|
184
|
+
if (taskArg.observedDigest !== runtimeArg.runtimeDigest) {
|
|
185
|
+
errors.push(`${label}.observedDigest must be the attested runtime digest`);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
const deploymentIds = tasks.map((taskArg) => (isPlainRecord(taskArg) ? taskArg.deploymentId : undefined));
|
|
189
|
+
const sortedDeploymentIds = deploymentIds
|
|
190
|
+
.filter((idArg) => typeof idArg === 'string')
|
|
191
|
+
.sort();
|
|
192
|
+
if (new Set(deploymentIds).size !== deploymentIds.length
|
|
193
|
+
|| JSON.stringify(deploymentIds) !== JSON.stringify(sortedDeploymentIds)) {
|
|
194
|
+
errors.push('runtime.tasks must be unique and sorted by deploymentId');
|
|
195
|
+
}
|
|
196
|
+
return errors;
|
|
197
|
+
};
|
|
198
|
+
export const validateLegacyServiceRuntimeAttestation = (attestationArg) => {
|
|
199
|
+
if (!isPlainRecord(attestationArg))
|
|
200
|
+
return ['attestation must be an object'];
|
|
201
|
+
const errors = [];
|
|
202
|
+
if (!hasExactKeys(attestationArg, ['id', 'data'])) {
|
|
203
|
+
errors.push('attestation must use its exact schema');
|
|
204
|
+
}
|
|
205
|
+
if (!isIdentifier(attestationArg.id)) {
|
|
206
|
+
errors.push('id must be a bounded canonical identifier');
|
|
207
|
+
}
|
|
208
|
+
const data = attestationArg.data;
|
|
209
|
+
if (!isPlainRecord(data))
|
|
210
|
+
return [...errors, 'data must be an object'];
|
|
211
|
+
if (!hasExactKeys(data, [
|
|
212
|
+
'legacyServiceId',
|
|
213
|
+
'organizationId',
|
|
214
|
+
'source',
|
|
215
|
+
'runtime',
|
|
216
|
+
'capturedByUserId',
|
|
217
|
+
'capturedAt',
|
|
218
|
+
])) {
|
|
219
|
+
errors.push('data must use its exact schema');
|
|
220
|
+
}
|
|
221
|
+
if (!isIdentifier(data.legacyServiceId)) {
|
|
222
|
+
errors.push('legacyServiceId must be a bounded canonical identifier');
|
|
223
|
+
}
|
|
224
|
+
if (!isIdentifier(data.organizationId)) {
|
|
225
|
+
errors.push('organizationId must be a bounded canonical identifier');
|
|
226
|
+
}
|
|
227
|
+
if (!isIdentifier(data.capturedByUserId)) {
|
|
228
|
+
errors.push('capturedByUserId must be a bounded canonical identifier');
|
|
229
|
+
}
|
|
230
|
+
if (!isPositiveSafeInteger(data.capturedAt, Number.MAX_SAFE_INTEGER)) {
|
|
231
|
+
errors.push('capturedAt must be a positive timestamp');
|
|
232
|
+
}
|
|
233
|
+
errors.push(...validateImageSource(data.source));
|
|
234
|
+
errors.push(...validateRuntime(data.runtime, isPlainRecord(data.source) ? data.source.rootDigest : undefined));
|
|
235
|
+
return errors;
|
|
236
|
+
};
|
|
237
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwbG95bWVudGxlZ2FjeWltcG9ydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2RhdGEvZGVwbG95bWVudGxlZ2FjeWltcG9ydC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFLQSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBbUczRSxNQUFNLGVBQWUsR0FBRyxxQ0FBcUMsQ0FBQztBQUM5RCxvR0FBb0c7QUFDcEcsTUFBTSxXQUFXLEdBQUcscUNBQXFDLENBQUM7QUFDMUQsTUFBTSxpQkFBaUIsR0FBRywwREFBMEQsQ0FBQztBQUNyRixNQUFNLGVBQWUsR0FBRyxxRkFBcUYsQ0FBQztBQUM5RyxNQUFNLG1CQUFtQixHQUFHLENBQUMsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDO0FBQzVDLE1BQU0sbUJBQW1CLEdBQUcsRUFBRSxDQUFDO0FBRS9CLE1BQU0scUNBQXFDLEdBQXFEO0lBQzlGLDRDQUE0QztJQUM1QyxzREFBc0Q7Q0FDdkQsQ0FBQztBQUNGLE1BQU0sZ0NBQWdDLEdBQWdEO0lBQ3BGLHlDQUF5QztJQUN6QywyREFBMkQ7Q0FDNUQsQ0FBQztBQUNGLE1BQU0sb0JBQW9CLEdBQTJDLENBQUMsYUFBYSxFQUFFLGFBQWEsQ0FBQyxDQUFDO0FBRXBHLE1BQU0sWUFBWSxHQUFHLENBQUMsUUFBaUIsRUFBc0IsRUFBRSxDQUFDLENBQzlELE9BQU8sUUFBUSxLQUFLLFFBQVEsSUFBSSxlQUFlLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUMvRCxDQUFDO0FBRUYsTUFBTSxpQkFBaUIsR0FBRyxDQUFDLFFBQWlCLEVBQTZCLEVBQUUsQ0FBQyxDQUMxRSxPQUFPLFFBQVEsS0FBSyxRQUFRLElBQUkscUJBQXFCLENBQUMsUUFBUSxDQUFDLEtBQUssUUFBUSxDQUM3RSxDQUFDO0FBRUYsTUFBTSxx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
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { IDeploymentPreflightProposedConfiguration, IDeploymentPreflightRuntimeTask } from './deploymentpreflight.js';
|
|
2
2
|
import type { IGatewayRouteDnsResult } from './gateway.js';
|
|
3
3
|
import type { IImageRolloutStatus, IImmutableImageDeploymentPlan, TSha256Digest } from './immutableimage.js';
|
|
4
|
+
import type { ILegacyImageImportReference } from './deploymentlegacyimport.js';
|
|
4
5
|
import type { IRegistryTarget } from './registry.js';
|
|
5
6
|
import type { TServiceTargetPortRef } from './serviceports.js';
|
|
6
7
|
export type TDeploymentOperationMode = 'existing-service' | 'greenfield';
|
|
@@ -9,24 +10,42 @@ interface IDeploymentReservationInputBase {
|
|
|
9
10
|
organizationId: string;
|
|
10
11
|
serviceId: string;
|
|
11
12
|
idempotencyKey: string;
|
|
12
|
-
sourceRevision: string;
|
|
13
13
|
version: string;
|
|
14
14
|
intentDigest: TSha256Digest;
|
|
15
15
|
proposedConfiguration: IDeploymentPreflightProposedConfiguration;
|
|
16
16
|
releaseTag: string;
|
|
17
17
|
routes: IDeploymentRouteRequest[];
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
/** A deployment whose image its deployer builds and pushes from one source revision. */
|
|
20
|
+
interface ISourceDeploymentOrigin {
|
|
21
|
+
sourceRevision: string;
|
|
22
|
+
legacyImport?: never;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A greenfield deployment whose image Cloudly imports from an attested legacy service instead:
|
|
26
|
+
* there is no source revision, so none is stated.
|
|
27
|
+
*/
|
|
28
|
+
interface ILegacyImportDeploymentOrigin {
|
|
29
|
+
legacyImport: ILegacyImageImportReference;
|
|
30
|
+
sourceRevision?: never;
|
|
31
|
+
}
|
|
32
|
+
export interface IExistingServiceDeploymentReservationInput extends IDeploymentReservationInputBase, ISourceDeploymentOrigin {
|
|
20
33
|
mode: 'existing-service';
|
|
21
34
|
expectedRolloutGeneration: number;
|
|
22
35
|
expectedCurrentRolloutId?: string;
|
|
23
36
|
}
|
|
24
|
-
export interface IGreenfieldServiceDeploymentReservationInput extends IDeploymentReservationInputBase {
|
|
37
|
+
export interface IGreenfieldServiceDeploymentReservationInput extends IDeploymentReservationInputBase, ISourceDeploymentOrigin {
|
|
25
38
|
mode: 'greenfield';
|
|
26
39
|
expectedRolloutGeneration?: never;
|
|
27
40
|
expectedCurrentRolloutId?: never;
|
|
28
41
|
}
|
|
29
|
-
|
|
42
|
+
/** A greenfield successor slot whose image is a legacy service's attested image. */
|
|
43
|
+
export interface ILegacyImportServiceDeploymentReservationInput extends IDeploymentReservationInputBase, ILegacyImportDeploymentOrigin {
|
|
44
|
+
mode: 'greenfield';
|
|
45
|
+
expectedRolloutGeneration?: never;
|
|
46
|
+
expectedCurrentRolloutId?: never;
|
|
47
|
+
}
|
|
48
|
+
export type IDeploymentReservationInput = IExistingServiceDeploymentReservationInput | IGreenfieldServiceDeploymentReservationInput | ILegacyImportServiceDeploymentReservationInput;
|
|
30
49
|
export type TDeploymentOperationState = 'reserving' | 'reserved' | 'provisioning' | 'awaiting-image' | 'image-recorded' | 'promoting-image' | 'rolling-out' | 'ready-for-route' | 'promoting-route' | 'verifying-route' | 'succeeded' | 'rolling-back' | 'rolled-back' | 'failed' | 'cleaning' | 'cleaned';
|
|
31
50
|
export interface IDeploymentRouteRequest {
|
|
32
51
|
hostname: string;
|
|
@@ -107,40 +126,46 @@ export interface IDeploymentOperationFailure {
|
|
|
107
126
|
export interface IServiceDeploymentOperation {
|
|
108
127
|
id: string;
|
|
109
128
|
revision: number;
|
|
110
|
-
data:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
129
|
+
data: IServiceDeploymentOperationData;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* An operation's durable state. It states where its image comes from exactly as its reservation
|
|
133
|
+
* did: a `sourceRevision` for an image its deployer builds, or a `legacyImport` (greenfield only)
|
|
134
|
+
* for a legacy service's attested image Cloudly imports.
|
|
135
|
+
*/
|
|
136
|
+
export type IServiceDeploymentOperationData = IServiceDeploymentOperationStateData & (ISourceDeploymentOrigin | ILegacyImportDeploymentOrigin);
|
|
137
|
+
interface IServiceDeploymentOperationStateData {
|
|
138
|
+
mode: TDeploymentOperationMode;
|
|
139
|
+
organizationId: string;
|
|
140
|
+
serviceId: string;
|
|
141
|
+
actorUserId: string;
|
|
142
|
+
idempotencyKey: string;
|
|
143
|
+
requestDigest: TSha256Digest;
|
|
144
|
+
version: string;
|
|
145
|
+
intentDigest: TSha256Digest;
|
|
146
|
+
configurationDigest: TSha256Digest;
|
|
147
|
+
proposedConfiguration: IDeploymentPreflightProposedConfiguration;
|
|
148
|
+
namespace: string;
|
|
149
|
+
registryTarget: IRegistryTarget;
|
|
150
|
+
releaseTag: string;
|
|
151
|
+
routes: IDeploymentRouteRequest[];
|
|
152
|
+
claimKeys: string[];
|
|
153
|
+
state: TDeploymentOperationState;
|
|
154
|
+
releaseId?: string;
|
|
155
|
+
registryRootDigest?: TSha256Digest;
|
|
156
|
+
rolloutId?: string;
|
|
157
|
+
rolloutGeneration?: number;
|
|
158
|
+
routeGeneration?: number;
|
|
159
|
+
routeVerification?: IDeploymentRouteVerificationEvidence[];
|
|
160
|
+
/**
|
|
161
|
+
* Most recent failure record. Set when the operation fails and retained
|
|
162
|
+
* as audit history through 'cleaning' and 'cleaned'; cleared only by a
|
|
163
|
+
* forward transition such as a successful retry. A populated failure does
|
|
164
|
+
* therefore NOT imply state === 'failed'.
|
|
165
|
+
*/
|
|
166
|
+
failure?: IDeploymentOperationFailure;
|
|
167
|
+
createdAt: number;
|
|
168
|
+
updatedAt: number;
|
|
144
169
|
}
|
|
145
170
|
export interface IServiceDeploymentStatus {
|
|
146
171
|
operation: IServiceDeploymentOperation;
|