@serve.zone/interfaces 22.0.0 → 23.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 +12 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/appstore/types.d.ts +31 -16
- package/dist_ts/appstore/types.js +208 -11
- package/dist_ts/data/deploymentoperation.js +6 -20
- package/dist_ts/data/deploymentpreflight.d.ts +2 -6
- package/dist_ts/data/deploymentpreflight.js +1 -1
- package/dist_ts/data/hostedapp.d.ts +40 -14
- package/dist_ts/data/hostedapp.js +90 -1
- package/dist_ts/data/immutableimage.d.ts +30 -1
- package/dist_ts/data/immutableimage.js +103 -1
- package/dist_ts/data/index.d.ts +0 -2
- package/dist_ts/data/index.js +1 -3
- package/dist_ts/data/secret.d.ts +186 -13
- package/dist_ts/data/secret.js +543 -45
- package/dist_ts/data/service.d.ts +0 -39
- package/dist_ts/data/service.js +1 -77
- package/dist_ts/data/settings.d.ts +62 -43
- package/dist_ts/data/settings.js +34 -2
- package/dist_ts/platform/storage.d.ts +13 -14
- package/dist_ts/platform/storage.js +1 -1
- package/dist_ts/platform/storagemigration.d.ts +8 -8
- package/dist_ts/platform/storagemigration.golden.d.ts +2 -2
- package/dist_ts/platform/storagemigration.golden.js +19 -88
- package/dist_ts/platform/storagemigration.js +46 -58
- package/dist_ts/platform/types.d.ts +5 -19
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/dist_ts/requests/hostedapp.d.ts +12 -9
- package/dist_ts/requests/hostedapp.js +38 -1
- package/dist_ts/requests/index.d.ts +1 -3
- package/dist_ts/requests/index.js +2 -4
- package/dist_ts/requests/platform.d.ts +0 -1
- package/dist_ts/requests/secret.d.ts +23 -17
- package/dist_ts/requests/secret.js +161 -2
- package/dist_ts/requests/service.d.ts +0 -13
- package/dist_ts/requests/settings.d.ts +3 -3
- package/dist_ts/runtime.d.ts +78 -26
- package/dist_ts/runtime.js +219 -19
- package/package.json +2 -1
- package/readme.md +106 -28
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/appstore/types.ts +251 -25
- package/ts/data/deploymentoperation.ts +7 -31
- package/ts/data/deploymentpreflight.ts +2 -5
- package/ts/data/hostedapp.ts +144 -18
- package/ts/data/immutableimage.ts +150 -1
- package/ts/data/index.ts +0 -2
- package/ts/data/secret.ts +775 -53
- package/ts/data/service.ts +0 -118
- package/ts/data/settings.ts +70 -80
- package/ts/platform/storage.ts +15 -16
- package/ts/platform/storagemigration.golden.ts +18 -95
- package/ts/platform/storagemigration.ts +57 -87
- package/ts/platform/types.ts +6 -19
- package/ts/plugins.ts +2 -0
- package/ts/requests/hostedapp.ts +50 -9
- package/ts/requests/index.ts +0 -4
- package/ts/requests/platform.ts +0 -1
- package/ts/requests/secret.ts +204 -9
- package/ts/requests/service.ts +0 -16
- package/ts/requests/settings.ts +3 -3
- package/ts/runtime.ts +314 -41
- package/dist_ts/data/secretbundle.d.ts +0 -57
- package/dist_ts/data/secretbundle.js +0 -2
- package/dist_ts/data/secretgroup.d.ts +0 -52
- package/dist_ts/data/secretgroup.js +0 -2
- package/dist_ts/requests/secretbundle.d.ts +0 -75
- package/dist_ts/requests/secretbundle.js +0 -4
- package/dist_ts/requests/secretgroup.d.ts +0 -52
- package/dist_ts/requests/secretgroup.js +0 -4
- package/ts/data/secretbundle.ts +0 -65
- package/ts/data/secretgroup.ts +0 -58
- package/ts/requests/secretbundle.ts +0 -103
- package/ts/requests/secretgroup.ts +0 -74
package/ts/data/service.ts
CHANGED
|
@@ -34,36 +34,12 @@ export interface IServiceVolume {
|
|
|
34
34
|
options?: Record<string, string>;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/**
|
|
38
|
-
* A single secret-bundle key delivered as a file instead of an environment
|
|
39
|
-
* variable. Targets are intentionally restricted to Docker's secret tmpfs so
|
|
40
|
-
* a deployment declaration cannot overwrite application or system files.
|
|
41
|
-
*/
|
|
42
|
-
export interface IServiceSecretFile {
|
|
43
|
-
/** Key in the service's merged secret bundles. */
|
|
44
|
-
sourceKey: string;
|
|
45
|
-
/** Absolute file path below /run/secrets/. */
|
|
46
|
-
targetPath: string;
|
|
47
|
-
/** Numeric container user identity. */
|
|
48
|
-
uid: number;
|
|
49
|
-
/** Numeric container group identity. */
|
|
50
|
-
gid: number;
|
|
51
|
-
/** Read-only POSIX mode. Supported values are 0400 and 0440. */
|
|
52
|
-
mode: number;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
37
|
export const serviceContainerArgLimits = {
|
|
56
38
|
maximumArgs: 64,
|
|
57
39
|
maximumArgBytes: 4096,
|
|
58
40
|
maximumTotalBytes: 32 * 1024,
|
|
59
41
|
} as const;
|
|
60
42
|
|
|
61
|
-
export const serviceSecretFileLimits = {
|
|
62
|
-
maximumFiles: 32,
|
|
63
|
-
maximumValueBytes: 500 * 1024,
|
|
64
|
-
maximumIdentity: 2_147_483_647,
|
|
65
|
-
} as const;
|
|
66
|
-
|
|
67
43
|
export const normalizeServiceAbsolutePath = (pathArg: string): string | undefined => {
|
|
68
44
|
if (typeof pathArg !== 'string'
|
|
69
45
|
|| !pathArg.startsWith('/')
|
|
@@ -85,85 +61,6 @@ export const normalizeServiceAbsolutePath = (pathArg: string): string | undefine
|
|
|
85
61
|
return `/${segments.join('/')}`;
|
|
86
62
|
};
|
|
87
63
|
|
|
88
|
-
const isCanonicalSecretTargetPath = (targetPathArg: string): boolean => (
|
|
89
|
-
normalizeServiceAbsolutePath(targetPathArg) === targetPathArg
|
|
90
|
-
&& /^\/run\/secrets\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(targetPathArg)
|
|
91
|
-
&& targetPathArg !== '/run/secrets/secret.json'
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
const pathsOverlap = (leftArg: string, rightArg: string): boolean => {
|
|
95
|
-
const left = normalizeServiceAbsolutePath(leftArg);
|
|
96
|
-
const right = normalizeServiceAbsolutePath(rightArg);
|
|
97
|
-
if (!left || !right) return false;
|
|
98
|
-
if (left === '/' || right === '/') return true;
|
|
99
|
-
return left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
/** Pure, secret-value-free validation shared by Cloudly and Coreflow. */
|
|
103
|
-
export const validateServiceSecretFiles = (
|
|
104
|
-
secretFilesArg: unknown,
|
|
105
|
-
volumeMountPathsArg: string[] = [],
|
|
106
|
-
): string[] => {
|
|
107
|
-
if (secretFilesArg === undefined) return [];
|
|
108
|
-
if (!Array.isArray(secretFilesArg)) return ['secretFiles must be an array'];
|
|
109
|
-
if (secretFilesArg.length > serviceSecretFileLimits.maximumFiles) {
|
|
110
|
-
return [`secretFiles must contain at most ${serviceSecretFileLimits.maximumFiles} entries`];
|
|
111
|
-
}
|
|
112
|
-
const errors: string[] = [];
|
|
113
|
-
if (volumeMountPathsArg.some((mountPathArg) => (
|
|
114
|
-
normalizeServiceAbsolutePath(mountPathArg) !== mountPathArg
|
|
115
|
-
))) {
|
|
116
|
-
errors.push('volume mount paths must be canonical absolute paths');
|
|
117
|
-
}
|
|
118
|
-
const sourceKeys: string[] = [];
|
|
119
|
-
const targetPaths: string[] = [];
|
|
120
|
-
for (const [index, candidate] of secretFilesArg.entries()) {
|
|
121
|
-
if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
|
|
122
|
-
errors.push(`secretFiles[${index}] must be an object`);
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
const mapping = candidate as Partial<IServiceSecretFile>;
|
|
126
|
-
if (typeof mapping.sourceKey !== 'string'
|
|
127
|
-
|| mapping.sourceKey.length > 253
|
|
128
|
-
|| !/^[A-Z_][A-Z0-9_]*$/.test(mapping.sourceKey)) {
|
|
129
|
-
errors.push(`secretFiles[${index}].sourceKey must be a canonical environment key`);
|
|
130
|
-
} else {
|
|
131
|
-
sourceKeys.push(mapping.sourceKey);
|
|
132
|
-
}
|
|
133
|
-
if (typeof mapping.targetPath !== 'string'
|
|
134
|
-
|| !isCanonicalSecretTargetPath(mapping.targetPath)) {
|
|
135
|
-
errors.push(
|
|
136
|
-
`secretFiles[${index}].targetPath must be a unique canonical file below /run/secrets and may not be secret.json`,
|
|
137
|
-
);
|
|
138
|
-
} else {
|
|
139
|
-
targetPaths.push(mapping.targetPath);
|
|
140
|
-
if (volumeMountPathsArg.some((mountPathArg) => (
|
|
141
|
-
typeof mountPathArg === 'string' && pathsOverlap(mapping.targetPath as string, mountPathArg)
|
|
142
|
-
))) {
|
|
143
|
-
errors.push(`secretFiles[${index}].targetPath overlaps a declared volume mount`);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
for (const identityField of ['uid', 'gid'] as const) {
|
|
147
|
-
const identity = mapping[identityField];
|
|
148
|
-
if (!Number.isSafeInteger(identity)
|
|
149
|
-
|| (identity as number) < 0
|
|
150
|
-
|| (identity as number) > serviceSecretFileLimits.maximumIdentity) {
|
|
151
|
-
errors.push(`secretFiles[${index}].${identityField} must be a bounded numeric identity`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (mapping.mode !== 0o400 && mapping.mode !== 0o440) {
|
|
155
|
-
errors.push(`secretFiles[${index}].mode must be 0400 or 0440`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
if (new Set(sourceKeys).size !== sourceKeys.length) {
|
|
159
|
-
errors.push('secretFiles sourceKey values must be unique');
|
|
160
|
-
}
|
|
161
|
-
if (new Set(targetPaths).size !== targetPaths.length) {
|
|
162
|
-
errors.push('secretFiles targetPath values must be unique');
|
|
163
|
-
}
|
|
164
|
-
return errors;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
64
|
/** Pure validation for the exact OCI/Docker argument vector after image entrypoint. */
|
|
168
65
|
export const validateServiceContainerArgs = (containerArgsArg: unknown): string[] => {
|
|
169
66
|
if (containerArgsArg === undefined) return [];
|
|
@@ -261,27 +158,12 @@ export interface IService {
|
|
|
261
158
|
hostedAppLifecycle?: IHostedAppLifecycleState;
|
|
262
159
|
reconciliationStatus?: IServiceReconciliationStatus;
|
|
263
160
|
environment: { [key: string]: string };
|
|
264
|
-
/**
|
|
265
|
-
* Legacy main SecretBundle ID. New services use secretConfiguration and
|
|
266
|
-
* the server-managed secretDeployment manifest pointer.
|
|
267
|
-
*
|
|
268
|
-
* @deprecated Compatibility-only until the versioned SecretBundle migration completes.
|
|
269
|
-
*/
|
|
270
|
-
secretBundleId?: string;
|
|
271
|
-
/**
|
|
272
|
-
* Legacy additional SecretBundle IDs.
|
|
273
|
-
*
|
|
274
|
-
* @deprecated Use explicit same-organization SecretSet attachments.
|
|
275
|
-
*/
|
|
276
|
-
additionalSecretBundleIds?: string[];
|
|
277
161
|
/** Value-free, caller-managed SecretSet attachment configuration. */
|
|
278
162
|
secretConfiguration?: IServiceSecretConfiguration;
|
|
279
163
|
/** Server-managed immutable manifest authority pointer. */
|
|
280
164
|
secretDeployment?: IServiceSecretDeploymentPlan;
|
|
281
165
|
/** Exact OCI/Docker argument vector appended after the image entrypoint. */
|
|
282
166
|
containerArgs?: string[];
|
|
283
|
-
/** Secret keys mounted as files and excluded from Env and secret.json. */
|
|
284
|
-
secretFiles?: IServiceSecretFile[];
|
|
285
167
|
|
|
286
168
|
/**
|
|
287
169
|
* Service category determines deployment behavior
|
package/ts/data/settings.ts
CHANGED
|
@@ -1,94 +1,84 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { ISystemSecretOwner, TSecretManagementScope } from './secret.js';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
export interface ICloudlyS3BackupTargetDescriptor {
|
|
4
|
+
type: 's3';
|
|
5
|
+
endpoint: string;
|
|
6
|
+
bucket: string;
|
|
7
|
+
region?: string;
|
|
8
|
+
port?: number;
|
|
9
|
+
useSsl?: boolean;
|
|
10
|
+
}
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
export interface ICloudlyNfsBackupTargetDescriptor {
|
|
13
|
+
type: 'nfs';
|
|
14
|
+
path: string;
|
|
15
|
+
}
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
backupExternalTargetConfig?: string;
|
|
17
|
+
export interface ICloudlySmbBackupTargetDescriptor {
|
|
18
|
+
type: 'smb';
|
|
19
|
+
host: string;
|
|
20
|
+
share: string;
|
|
21
|
+
domain?: string;
|
|
22
|
+
port?: number;
|
|
23
|
+
}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
export type TCloudlyBackupTargetDescriptor =
|
|
26
|
+
| ICloudlyS3BackupTargetDescriptor
|
|
27
|
+
| ICloudlyNfsBackupTargetDescriptor
|
|
28
|
+
| ICloudlySmbBackupTargetDescriptor;
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
/** Public runtime-configurable settings. No serialized credential belongs here. */
|
|
31
|
+
export interface ICloudlySettings {
|
|
32
|
+
deploymentArchiveRetentionDays?: number;
|
|
33
|
+
backupExternalTarget?: TCloudlyBackupTargetDescriptor;
|
|
34
|
+
backupNodeCacheKeepDays?: number;
|
|
30
35
|
dcrouterGatewayUrl?: string;
|
|
31
|
-
dcrouterGatewayApiToken?: string;
|
|
32
36
|
dcrouterGatewayClientId?: string;
|
|
33
37
|
dcrouterTargetHost?: string;
|
|
34
|
-
dcrouterTargetPort?:
|
|
35
|
-
dcrouterTargetHostsByNode?: string
|
|
36
|
-
|
|
37
|
-
// External dcrouter mail integration. Submission settings are injected into
|
|
38
|
-
// service secret bundles; forward targets are used for inbound smtpForward.
|
|
38
|
+
dcrouterTargetPort?: number;
|
|
39
|
+
dcrouterTargetHostsByNode?: Record<string, string>;
|
|
39
40
|
dcrouterMailSubmissionHost?: string;
|
|
40
|
-
dcrouterMailSubmissionPort?:
|
|
41
|
+
dcrouterMailSubmissionPort?: number;
|
|
41
42
|
dcrouterMailSubmissionTlsMode?: 'plain' | 'starttls' | 'implicitTls';
|
|
42
43
|
dcrouterMailForwardTargetHost?: string;
|
|
43
|
-
dcrouterMailForwardTargetHostsByNode?: string
|
|
44
|
-
|
|
45
|
-
// BaseOS enrollment
|
|
46
|
-
baseosJoinToken?: string;
|
|
47
|
-
|
|
48
|
-
// CoreBuild worker used for image generation
|
|
44
|
+
dcrouterMailForwardTargetHostsByNode?: Record<string, string>;
|
|
49
45
|
corebuildWorkerUrl?: string;
|
|
50
|
-
|
|
51
|
-
corebuildWorkersJson?: string;
|
|
52
|
-
|
|
53
|
-
// AWS Credentials
|
|
54
|
-
awsAccessKey?: string;
|
|
55
|
-
awsSecretKey?: string;
|
|
56
|
-
awsRegion?: string;
|
|
57
|
-
|
|
58
|
-
// DigitalOcean
|
|
59
|
-
digitalOceanToken?: string;
|
|
60
|
-
|
|
61
|
-
// Azure Credentials
|
|
62
|
-
azureClientId?: string;
|
|
63
|
-
azureClientSecret?: string;
|
|
64
|
-
azureTenantId?: string;
|
|
65
|
-
azureSubscriptionId?: string;
|
|
66
|
-
|
|
67
|
-
// Google Cloud
|
|
68
|
-
googleCloudKeyJson?: string;
|
|
69
|
-
googleCloudProjectId?: string;
|
|
70
|
-
|
|
71
|
-
// Vultr
|
|
72
|
-
vultrApiKey?: string;
|
|
73
|
-
|
|
74
|
-
// Linode
|
|
75
|
-
linodeToken?: string;
|
|
76
|
-
|
|
77
|
-
// OVH
|
|
78
|
-
ovhApplicationKey?: string;
|
|
79
|
-
ovhApplicationSecret?: string;
|
|
80
|
-
ovhConsumerKey?: string;
|
|
81
|
-
|
|
82
|
-
// Scaleway
|
|
83
|
-
scalewayAccessKey?: string;
|
|
84
|
-
scalewaySecretKey?: string;
|
|
85
|
-
scalewayOrganizationId?: string;
|
|
46
|
+
corebuildWorkerUrls?: string[];
|
|
86
47
|
}
|
|
87
48
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
49
|
+
export const cloudlySystemSecretOwner = Object.freeze({
|
|
50
|
+
kind: 'system',
|
|
51
|
+
systemId: 'cloudly-settings',
|
|
52
|
+
} as const satisfies ISystemSecretOwner);
|
|
53
|
+
|
|
54
|
+
export const cloudlySystemSecretManagementScope =
|
|
55
|
+
'system:cloudly-settings' as const satisfies TSecretManagementScope;
|
|
56
|
+
|
|
57
|
+
/** Stable keys for values removed from serialized Cloudly settings. */
|
|
58
|
+
export const cloudlySystemSecretKeys = Object.freeze({
|
|
59
|
+
hetznerToken: 'HETZNER_TOKEN',
|
|
60
|
+
cloudflareToken: 'CLOUDFLARE_TOKEN',
|
|
61
|
+
backupExternalTargetCredentials: 'BACKUP_EXTERNAL_TARGET_CREDENTIALS',
|
|
62
|
+
dcrouterGatewayApiToken: 'DCROUTER_GATEWAY_API_TOKEN',
|
|
63
|
+
baseosJoinToken: 'BASEOS_JOIN_TOKEN',
|
|
64
|
+
corebuildWorkerToken: 'COREBUILD_WORKER_TOKEN',
|
|
65
|
+
corebuildWorkers: 'COREBUILD_WORKERS',
|
|
66
|
+
awsAccessKey: 'AWS_ACCESS_KEY',
|
|
67
|
+
awsSecretKey: 'AWS_SECRET_KEY',
|
|
68
|
+
awsRegion: 'AWS_REGION',
|
|
69
|
+
digitalOceanToken: 'DIGITALOCEAN_TOKEN',
|
|
70
|
+
azureClientId: 'AZURE_CLIENT_ID',
|
|
71
|
+
azureClientSecret: 'AZURE_CLIENT_SECRET',
|
|
72
|
+
azureTenantId: 'AZURE_TENANT_ID',
|
|
73
|
+
azureSubscriptionId: 'AZURE_SUBSCRIPTION_ID',
|
|
74
|
+
googleCloudKey: 'GOOGLE_CLOUD_KEY',
|
|
75
|
+
googleCloudProjectId: 'GOOGLE_CLOUD_PROJECT_ID',
|
|
76
|
+
vultrApiKey: 'VULTR_API_KEY',
|
|
77
|
+
linodeToken: 'LINODE_TOKEN',
|
|
78
|
+
ovhApplicationKey: 'OVH_APPLICATION_KEY',
|
|
79
|
+
ovhApplicationSecret: 'OVH_APPLICATION_SECRET',
|
|
80
|
+
ovhConsumerKey: 'OVH_CONSUMER_KEY',
|
|
81
|
+
scalewayAccessKey: 'SCALEWAY_ACCESS_KEY',
|
|
82
|
+
scalewaySecretKey: 'SCALEWAY_SECRET_KEY',
|
|
83
|
+
scalewayOrganizationId: 'SCALEWAY_ORGANIZATION_ID',
|
|
84
|
+
} as const);
|
package/ts/platform/storage.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { TSecretManagementScope } from '../data/secret.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Backend-neutral storage vocabulary shared by App Store manifests, control
|
|
3
5
|
* planes, and fulfillment adapters. Physical providers and runtime attachment
|
|
@@ -100,13 +102,13 @@ export type TStorageClassCapabilities =
|
|
|
100
102
|
* addresses, mount options, principals, and credential values are private.
|
|
101
103
|
*/
|
|
102
104
|
export interface IStorageCapabilityAdvertisement {
|
|
103
|
-
schemaVersion:
|
|
105
|
+
schemaVersion: 2;
|
|
104
106
|
featureIds: string[];
|
|
105
107
|
classes: TStorageClassCapabilities[];
|
|
106
108
|
}
|
|
107
109
|
|
|
108
|
-
export interface
|
|
109
|
-
type: 'environment';
|
|
110
|
+
export interface IObjectStorageLauncherEnvironmentDelivery {
|
|
111
|
+
type: 'launcher-environment';
|
|
110
112
|
/**
|
|
111
113
|
* Explicit target keys prevent two named bindings from silently overwriting
|
|
112
114
|
* each other. Every required field must have a distinct environment key.
|
|
@@ -121,8 +123,8 @@ export interface IObjectStorageEnvironmentDelivery {
|
|
|
121
123
|
};
|
|
122
124
|
}
|
|
123
125
|
|
|
124
|
-
export interface
|
|
125
|
-
type: '
|
|
126
|
+
export interface IObjectStorageFileDelivery {
|
|
127
|
+
type: 'file';
|
|
126
128
|
/** Canonical absolute path below /run/secrets/. */
|
|
127
129
|
targetPath: string;
|
|
128
130
|
/**
|
|
@@ -130,11 +132,14 @@ export interface IObjectStorageSecretFileDelivery {
|
|
|
130
132
|
* secretAccessKey, and optional sessionToken fields.
|
|
131
133
|
*/
|
|
132
134
|
format: 'servezone-object-storage-v1';
|
|
135
|
+
uid: number;
|
|
136
|
+
gid: number;
|
|
137
|
+
mode: 0o400 | 0o440;
|
|
133
138
|
}
|
|
134
139
|
|
|
135
140
|
export type TObjectStorageDelivery =
|
|
136
|
-
|
|
|
137
|
-
|
|
|
141
|
+
| IObjectStorageLauncherEnvironmentDelivery
|
|
142
|
+
| IObjectStorageFileDelivery;
|
|
138
143
|
|
|
139
144
|
export type TResolvedStorageBindingStatus =
|
|
140
145
|
| 'requested'
|
|
@@ -176,7 +181,7 @@ export interface IResolvedStorageCapacity {
|
|
|
176
181
|
}
|
|
177
182
|
|
|
178
183
|
export interface IResolvedStorageBindingBase {
|
|
179
|
-
schemaVersion:
|
|
184
|
+
schemaVersion: 2;
|
|
180
185
|
/** Stable binding/allocation identity. */
|
|
181
186
|
id: string;
|
|
182
187
|
serviceId: string;
|
|
@@ -208,13 +213,6 @@ extends IResolvedStorageBindingBase {
|
|
|
208
213
|
accessMode: TFilesystemStorageAccessMode;
|
|
209
214
|
}
|
|
210
215
|
|
|
211
|
-
export interface IStorageObjectCredentialsRef {
|
|
212
|
-
secretBundleId: string;
|
|
213
|
-
accessKeyIdKey: string;
|
|
214
|
-
secretAccessKeyKey: string;
|
|
215
|
-
sessionTokenKey?: string;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
216
|
export interface IResolvedObjectStorageConnection {
|
|
219
217
|
endpoint: string;
|
|
220
218
|
bucket: string;
|
|
@@ -226,7 +224,8 @@ extends IResolvedStorageBindingBase {
|
|
|
226
224
|
kind: 'objectStorage';
|
|
227
225
|
accessMode: TObjectStorageAccessMode;
|
|
228
226
|
connection: IResolvedObjectStorageConnection;
|
|
229
|
-
|
|
227
|
+
/** Value-free service-owned Secret reconciliation scope. */
|
|
228
|
+
credentialManagementScope: Extract<TSecretManagementScope, `platform:${string}`>;
|
|
230
229
|
delivery: TObjectStorageDelivery;
|
|
231
230
|
versioning: boolean;
|
|
232
231
|
retentionDays?: number;
|
|
@@ -24,7 +24,7 @@ const capabilities = {
|
|
|
24
24
|
} as const;
|
|
25
25
|
|
|
26
26
|
const delivery = {
|
|
27
|
-
type: 'environment',
|
|
27
|
+
type: 'launcher-environment',
|
|
28
28
|
keys: {
|
|
29
29
|
endpoint: 'APP_S3_ENDPOINT',
|
|
30
30
|
bucket: 'APP_S3_BUCKET',
|
|
@@ -34,14 +34,10 @@ const delivery = {
|
|
|
34
34
|
},
|
|
35
35
|
} as const;
|
|
36
36
|
|
|
37
|
-
const
|
|
38
|
-
secretBundleId: 'service-storage',
|
|
39
|
-
accessKeyIdKey: 'object.accessKeyId',
|
|
40
|
-
secretAccessKeyKey: 'object.secretAccessKey',
|
|
41
|
-
};
|
|
37
|
+
const credentialManagementScope = 'platform:object-storage-binding';
|
|
42
38
|
|
|
43
39
|
const sourceBinding: TActiveObjectStorageBinding = {
|
|
44
|
-
schemaVersion:
|
|
40
|
+
schemaVersion: 2,
|
|
45
41
|
id: 'binding-object',
|
|
46
42
|
serviceId: 'service-app',
|
|
47
43
|
requestId: 'object-data',
|
|
@@ -63,7 +59,7 @@ const sourceBinding: TActiveObjectStorageBinding = {
|
|
|
63
59
|
bucket: 'source-bucket',
|
|
64
60
|
region: 'local',
|
|
65
61
|
},
|
|
66
|
-
|
|
62
|
+
credentialManagementScope,
|
|
67
63
|
delivery,
|
|
68
64
|
versioning: false,
|
|
69
65
|
createdAt: 1_700_000_000_000,
|
|
@@ -96,12 +92,12 @@ const target = {
|
|
|
96
92
|
accessMode: 'readWrite',
|
|
97
93
|
delivery,
|
|
98
94
|
},
|
|
99
|
-
|
|
95
|
+
credentialManagementScope,
|
|
100
96
|
} as const;
|
|
101
97
|
|
|
102
98
|
const preparationSnapshotPayload:
|
|
103
99
|
TUnfencedObjectStorageBindingControlSnapshotDigestPayload = {
|
|
104
|
-
schemaVersion:
|
|
100
|
+
schemaVersion: 2,
|
|
105
101
|
binding: sourceBinding,
|
|
106
102
|
recordRevision: 12,
|
|
107
103
|
capturedAt: 1_700_000_000_010,
|
|
@@ -110,11 +106,11 @@ TUnfencedObjectStorageBindingControlSnapshotDigestPayload = {
|
|
|
110
106
|
const preparationSnapshot: IUnfencedObjectStorageBindingControlSnapshot = {
|
|
111
107
|
...preparationSnapshotPayload,
|
|
112
108
|
snapshotDigest:
|
|
113
|
-
'
|
|
109
|
+
'ac8166a9888144d11dc7419f60be055eeb6f392ec41a254a9f9b2a7b3df1fba7',
|
|
114
110
|
};
|
|
115
111
|
|
|
116
112
|
const prepareRequest: IObjectStorageMigrationPrepareRequest = {
|
|
117
|
-
schemaVersion:
|
|
113
|
+
schemaVersion: 2,
|
|
118
114
|
migrationId: 'migration-object-capacity',
|
|
119
115
|
sourceControlSnapshot: preparationSnapshot,
|
|
120
116
|
workloadGeneration: 8,
|
|
@@ -124,7 +120,7 @@ const prepareRequest: IObjectStorageMigrationPrepareRequest = {
|
|
|
124
120
|
const candidateBinding: TStagedObjectStorageBinding = {
|
|
125
121
|
...sourceBinding,
|
|
126
122
|
requestDigest:
|
|
127
|
-
'
|
|
123
|
+
'3f864862041002e25b790b3bc7afbe37c399a006f385e1def9f679a93d4fa173',
|
|
128
124
|
generation: 4,
|
|
129
125
|
observedGeneration: 3,
|
|
130
126
|
status: 'provisioning',
|
|
@@ -144,98 +140,25 @@ const candidateBinding: TStagedObjectStorageBinding = {
|
|
|
144
140
|
};
|
|
145
141
|
|
|
146
142
|
export const storageMigrationCanonicalDigestGoldenVectors = deepFreezeValue({
|
|
147
|
-
version:
|
|
143
|
+
version: 2 as const,
|
|
148
144
|
targetRequest: {
|
|
149
145
|
input: target.request,
|
|
150
|
-
canonicalJson:
|
|
151
|
-
|
|
152
|
-
'"delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET",' +
|
|
153
|
-
'"endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION",' +
|
|
154
|
-
'"secretAccessKey":"APP_S3_SECRET_KEY"},"type":"environment"},' +
|
|
155
|
-
'"id":"object-data","kind":"objectStorage","reclaimPolicy":"retain",' +
|
|
156
|
-
'"storageClass":"backupCapacity"}',
|
|
157
|
-
sha256:
|
|
158
|
-
'2096dfa1f6db50d670cbb4d6436f38c2eec383435c99346e31d0d1a9e0b1beba',
|
|
146
|
+
canonicalJson: '{"accessMode":"readWrite","capacity":{"limit":"1TiB","request":"500GiB"},"delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET","endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION","secretAccessKey":"APP_S3_SECRET_KEY"},"type":"launcher-environment"},"id":"object-data","kind":"objectStorage","reclaimPolicy":"retain","storageClass":"backupCapacity"}',
|
|
147
|
+
sha256: '3f864862041002e25b790b3bc7afbe37c399a006f385e1def9f679a93d4fa173',
|
|
159
148
|
},
|
|
160
149
|
preparationSnapshot: {
|
|
161
150
|
input: preparationSnapshotPayload,
|
|
162
|
-
canonicalJson:
|
|
163
|
-
|
|
164
|
-
'"durability":"persistent","encryptedInTransit":true,"hardQuota":true,' +
|
|
165
|
-
'"performanceTier":"capacity","snapshots":"none","topology":"singleNode"},' +
|
|
166
|
-
'"connection":{"bucket":"source-bucket","endpoint":"https://source.example.test",' +
|
|
167
|
-
'"region":"local"},"createdAt":1700000000000,"credentials":' +
|
|
168
|
-
'{"accessKeyIdKey":"object.accessKeyId","secretAccessKeyKey":"object.secretAccessKey",' +
|
|
169
|
-
'"secretBundleId":"service-storage"},"delivery":{"keys":' +
|
|
170
|
-
'{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET",' +
|
|
171
|
-
'"endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION",' +
|
|
172
|
-
'"secretAccessKey":"APP_S3_SECRET_KEY"},"type":"environment"},' +
|
|
173
|
-
'"generation":3,"id":"binding-object","kind":"objectStorage",' +
|
|
174
|
-
'"observedGeneration":3,"policy":{"classId":"source-object","revision":"1"},' +
|
|
175
|
-
'"requestDigest":"1111111111111111111111111111111111111111111111111111111111111111",' +
|
|
176
|
-
'"requestId":"object-data","resourceRef":"source-resource","schemaVersion":1,' +
|
|
177
|
-
'"serviceId":"service-app","status":"ready","updatedAt":1700000000001,' +
|
|
178
|
-
'"versioning":false},"capturedAt":1700000000010,"recordRevision":12,"schemaVersion":1}',
|
|
179
|
-
sha256:
|
|
180
|
-
'35870a0c3ca7f2ee70d0ae7f83109bec23a8ce59bf8efef1c94221a496210439',
|
|
151
|
+
canonicalJson: '{"binding":{"accessMode":"readWrite","capabilities":{"backup":true,"durability":"persistent","encryptedInTransit":true,"hardQuota":true,"performanceTier":"capacity","snapshots":"none","topology":"singleNode"},"connection":{"bucket":"source-bucket","endpoint":"https://source.example.test","region":"local"},"createdAt":1700000000000,"credentialManagementScope":"platform:object-storage-binding","delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET","endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION","secretAccessKey":"APP_S3_SECRET_KEY"},"type":"launcher-environment"},"generation":3,"id":"binding-object","kind":"objectStorage","observedGeneration":3,"policy":{"classId":"source-object","revision":"1"},"requestDigest":"1111111111111111111111111111111111111111111111111111111111111111","requestId":"object-data","resourceRef":"source-resource","schemaVersion":2,"serviceId":"service-app","status":"ready","updatedAt":1700000000001,"versioning":false},"capturedAt":1700000000010,"recordRevision":12,"schemaVersion":2}',
|
|
152
|
+
sha256: 'ac8166a9888144d11dc7419f60be055eeb6f392ec41a254a9f9b2a7b3df1fba7',
|
|
181
153
|
},
|
|
182
154
|
prepareRequest: {
|
|
183
155
|
input: prepareRequest,
|
|
184
|
-
canonicalJson:
|
|
185
|
-
|
|
186
|
-
'"sourceControlSnapshot":{"binding":{"accessMode":"readWrite",' +
|
|
187
|
-
'"capabilities":{"backup":true,"durability":"persistent",' +
|
|
188
|
-
'"encryptedInTransit":true,"hardQuota":true,"performanceTier":"capacity",' +
|
|
189
|
-
'"snapshots":"none","topology":"singleNode"},"connection":' +
|
|
190
|
-
'{"bucket":"source-bucket","endpoint":"https://source.example.test","region":"local"},' +
|
|
191
|
-
'"createdAt":1700000000000,"credentials":{"accessKeyIdKey":"object.accessKeyId",' +
|
|
192
|
-
'"secretAccessKeyKey":"object.secretAccessKey","secretBundleId":"service-storage"},' +
|
|
193
|
-
'"delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET",' +
|
|
194
|
-
'"endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION",' +
|
|
195
|
-
'"secretAccessKey":"APP_S3_SECRET_KEY"},"type":"environment"},' +
|
|
196
|
-
'"generation":3,"id":"binding-object","kind":"objectStorage",' +
|
|
197
|
-
'"observedGeneration":3,"policy":{"classId":"source-object","revision":"1"},' +
|
|
198
|
-
'"requestDigest":"1111111111111111111111111111111111111111111111111111111111111111",' +
|
|
199
|
-
'"requestId":"object-data","resourceRef":"source-resource","schemaVersion":1,' +
|
|
200
|
-
'"serviceId":"service-app","status":"ready","updatedAt":1700000000001,' +
|
|
201
|
-
'"versioning":false},"capturedAt":1700000000010,"recordRevision":12,' +
|
|
202
|
-
'"schemaVersion":1,"snapshotDigest":' +
|
|
203
|
-
'"35870a0c3ca7f2ee70d0ae7f83109bec23a8ce59bf8efef1c94221a496210439"},' +
|
|
204
|
-
'"target":{"credentials":{"accessKeyIdKey":"object.accessKeyId",' +
|
|
205
|
-
'"secretAccessKeyKey":"object.secretAccessKey","secretBundleId":"service-storage"},' +
|
|
206
|
-
'"policy":{"classId":"capacity-object","revision":"4"},"request":' +
|
|
207
|
-
'{"accessMode":"readWrite","capacity":{"limit":"1TiB","request":"500GiB"},' +
|
|
208
|
-
'"delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET",' +
|
|
209
|
-
'"endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION",' +
|
|
210
|
-
'"secretAccessKey":"APP_S3_SECRET_KEY"},"type":"environment"},' +
|
|
211
|
-
'"id":"object-data","kind":"objectStorage","reclaimPolicy":"retain",' +
|
|
212
|
-
'"storageClass":"backupCapacity"},"storageClass":{"kind":"objectStorage",' +
|
|
213
|
-
'"purpose":"backup","required":{"backup":true,"durability":"persistent",' +
|
|
214
|
-
'"performanceTier":"capacity"}}},"workloadGeneration":8}',
|
|
215
|
-
sha256:
|
|
216
|
-
'ecbb40ec9bfa20b9f4372eb0d00eaf79c3e84dd3132c5bde7e65ba3fec55e595',
|
|
156
|
+
canonicalJson: '{"migrationId":"migration-object-capacity","schemaVersion":2,"sourceControlSnapshot":{"binding":{"accessMode":"readWrite","capabilities":{"backup":true,"durability":"persistent","encryptedInTransit":true,"hardQuota":true,"performanceTier":"capacity","snapshots":"none","topology":"singleNode"},"connection":{"bucket":"source-bucket","endpoint":"https://source.example.test","region":"local"},"createdAt":1700000000000,"credentialManagementScope":"platform:object-storage-binding","delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET","endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION","secretAccessKey":"APP_S3_SECRET_KEY"},"type":"launcher-environment"},"generation":3,"id":"binding-object","kind":"objectStorage","observedGeneration":3,"policy":{"classId":"source-object","revision":"1"},"requestDigest":"1111111111111111111111111111111111111111111111111111111111111111","requestId":"object-data","resourceRef":"source-resource","schemaVersion":2,"serviceId":"service-app","status":"ready","updatedAt":1700000000001,"versioning":false},"capturedAt":1700000000010,"recordRevision":12,"schemaVersion":2,"snapshotDigest":"ac8166a9888144d11dc7419f60be055eeb6f392ec41a254a9f9b2a7b3df1fba7"},"target":{"credentialManagementScope":"platform:object-storage-binding","policy":{"classId":"capacity-object","revision":"4"},"request":{"accessMode":"readWrite","capacity":{"limit":"1TiB","request":"500GiB"},"delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET","endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION","secretAccessKey":"APP_S3_SECRET_KEY"},"type":"launcher-environment"},"id":"object-data","kind":"objectStorage","reclaimPolicy":"retain","storageClass":"backupCapacity"},"storageClass":{"kind":"objectStorage","purpose":"backup","required":{"backup":true,"durability":"persistent","performanceTier":"capacity"}}},"workloadGeneration":8}',
|
|
157
|
+
sha256: '7682ed6f8c8b64d4c677ea91c04e4261bfe5326a3fb905d5da093f51e4b2dbbe',
|
|
217
158
|
},
|
|
218
159
|
candidateBinding: {
|
|
219
160
|
input: candidateBinding,
|
|
220
|
-
canonicalJson:
|
|
221
|
-
|
|
222
|
-
'"durability":"persistent","encryptedInTransit":true,"hardQuota":true,' +
|
|
223
|
-
'"performanceTier":"capacity","snapshots":"none","topology":"singleNode"},' +
|
|
224
|
-
'"capacity":{"granted":"500GiB","limit":"1TiB","requested":"500GiB"},' +
|
|
225
|
-
'"connection":{"bucket":"destination-bucket",' +
|
|
226
|
-
'"endpoint":"https://destination.example.test","region":"local"},' +
|
|
227
|
-
'"createdAt":1700000000000,"credentials":{"accessKeyIdKey":"object.accessKeyId",' +
|
|
228
|
-
'"secretAccessKeyKey":"object.secretAccessKey","secretBundleId":"service-storage"},' +
|
|
229
|
-
'"delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET",' +
|
|
230
|
-
'"endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION",' +
|
|
231
|
-
'"secretAccessKey":"APP_S3_SECRET_KEY"},"type":"environment"},' +
|
|
232
|
-
'"generation":4,"id":"binding-object","kind":"objectStorage",' +
|
|
233
|
-
'"observedGeneration":3,"policy":{"classId":"capacity-object","revision":"4"},' +
|
|
234
|
-
'"requestDigest":"2096dfa1f6db50d670cbb4d6436f38c2eec383435c99346e31d0d1a9e0b1beba",' +
|
|
235
|
-
'"requestId":"object-data","resourceRef":"destination-resource",' +
|
|
236
|
-
'"schemaVersion":1,"serviceId":"service-app","status":"provisioning",' +
|
|
237
|
-
'"updatedAt":1700000000020,"versioning":false}',
|
|
238
|
-
sha256:
|
|
239
|
-
'b49017f41cd86626ccb05ffa5fb591994a5e20fad9856182b1a1d4c964c6177c',
|
|
161
|
+
canonicalJson: '{"accessMode":"readWrite","capabilities":{"backup":true,"durability":"persistent","encryptedInTransit":true,"hardQuota":true,"performanceTier":"capacity","snapshots":"none","topology":"singleNode"},"capacity":{"granted":"500GiB","limit":"1TiB","requested":"500GiB"},"connection":{"bucket":"destination-bucket","endpoint":"https://destination.example.test","region":"local"},"createdAt":1700000000000,"credentialManagementScope":"platform:object-storage-binding","delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET","endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION","secretAccessKey":"APP_S3_SECRET_KEY"},"type":"launcher-environment"},"generation":4,"id":"binding-object","kind":"objectStorage","observedGeneration":3,"policy":{"classId":"capacity-object","revision":"4"},"requestDigest":"3f864862041002e25b790b3bc7afbe37c399a006f385e1def9f679a93d4fa173","requestId":"object-data","resourceRef":"destination-resource","schemaVersion":2,"serviceId":"service-app","status":"provisioning","updatedAt":1700000000020,"versioning":false}',
|
|
162
|
+
sha256: '6e5c954466b600f81d1f19dcdd7ef426be99dc12ae33103e4d0c238e0070de71',
|
|
240
163
|
},
|
|
241
164
|
});
|