@kumori/aurora-backend-handler 1.0.100 → 1.0.112
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/.gitlab-ci.yml +21 -21
- package/CHANGELOG.md +10 -0
- package/api/account-api-service.ts +35 -27
- package/api/deploy-service-helper.ts +72 -33
- package/api/environment-api-service.ts +20 -15
- package/api/marketplace-api-service.ts +32 -81
- package/api/resources-api-service.ts +28 -28
- package/api/service-api-service.ts +26 -22
- package/api/tenant-api-service.ts +145 -257
- package/api/user-api-service.ts +27 -12
- package/backend-handler.ts +373 -348
- package/developer.md +998 -0
- package/event-helper.ts +13 -3
- package/event-names.ts +3 -0
- package/helpers/account-helper.ts +15 -15
- package/helpers/environment-helper.ts +27 -30
- package/helpers/marketplace-helper.ts +109 -0
- package/helpers/resource-helper.ts +19 -1
- package/helpers/revision-helper.ts +68 -18
- package/helpers/service-helper.ts +49 -33
- package/helpers/tenant-helper.ts +0 -5
- package/helpers/token-helper.ts +74 -2
- package/helpers/user-helper.ts +71 -31
- package/jest.config.ts +1 -0
- package/package.json +3 -3
- package/test/backend-handler.test.ts +992 -730
- package/test/deploy-service-helper.test.ts +707 -273
- package/test/event-helper.test.ts +489 -104
- package/test/event-names.test.ts +186 -0
- package/websocket-manager.ts +369 -66
package/event-helper.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { loadUser } from './api/user-api-service';
|
|
1
2
|
import { Account, Environment, Link, MarketplaceItem, MarketplaceService, Organization, PlanProvider, Registry, Tenant, tenantRole, User, Service, Notification, Resource } from "@kumori/aurora-interfaces";
|
|
2
3
|
import { EventNames } from "./event-names";
|
|
3
4
|
export interface RegistryUpdatePayload {
|
|
@@ -63,7 +64,7 @@ export class EventHelper {
|
|
|
63
64
|
updateInvite: this.createEvent<{ tenant: string; user: string; role: tenantRole; }>(EventNames.UpdateInvite).publish,
|
|
64
65
|
inviteUpdated: this.createEvent<{ tenant: string; user: string; role: tenantRole; }>(EventNames.InviteUpdated).publish,
|
|
65
66
|
inviteUpdateError: this.createEvent<{ tenant: string; user: string; role: tenantRole; }>(EventNames.InviteUpdateError).publish,
|
|
66
|
-
rejectInvite: this.createEvent<{ tenant: string; }>(EventNames.RejectInvite).publish,
|
|
67
|
+
rejectInvite: this.createEvent<{ tenant: string; leave: boolean }>(EventNames.RejectInvite).publish,
|
|
67
68
|
inviteRejected: this.createEvent<{ user: string; tenant: string; }>(EventNames.InviteRejected).publish,
|
|
68
69
|
inviteRejectError: this.createEvent<{ tenant: string; }>(EventNames.InviteRejectError).publish,
|
|
69
70
|
createToken: this.createEvent<{ tenant: string; expiration: string; description: string }>(EventNames.CreateToken).publish,
|
|
@@ -104,7 +105,7 @@ export class EventHelper {
|
|
|
104
105
|
updateInvite: this.createEvent<{ tenant: string; user: string; role: tenantRole; }>(EventNames.UpdateInvite).subscribe,
|
|
105
106
|
inviteUpdated: this.createEvent<{ tenant: string; user: string; role: tenantRole; }>(EventNames.InviteUpdated).subscribe,
|
|
106
107
|
inviteUpdateError: this.createEvent<{ tenant: string; user: string; role: tenantRole; }>(EventNames.InviteUpdateError).subscribe,
|
|
107
|
-
rejectInvite: this.createEvent<{ tenant: string; }>(EventNames.RejectInvite).subscribe,
|
|
108
|
+
rejectInvite: this.createEvent<{ tenant: string; leave: boolean }>(EventNames.RejectInvite).subscribe,
|
|
108
109
|
inviteRejected: this.createEvent<{ user: string; tenant: string; }>(EventNames.InviteRejected).subscribe,
|
|
109
110
|
inviteRejectError: this.createEvent<{ tenant: string; }>(EventNames.InviteRejectError).subscribe,
|
|
110
111
|
createToken: this.createEvent<{ tenant: string; expiration: string; description: string }>(EventNames.CreateToken).subscribe,
|
|
@@ -145,7 +146,7 @@ export class EventHelper {
|
|
|
145
146
|
updateInvite: this.createEvent<{ tenant: string; user: string; role: tenantRole; }>(EventNames.UpdateInvite).unsubscribe,
|
|
146
147
|
inviteUpdated: this.createEvent<{ tenant: string; user: string; role: tenantRole; }>(EventNames.InviteUpdated).unsubscribe,
|
|
147
148
|
inviteUpdateError: this.createEvent<{ tenant: string; user: string; role: tenantRole; }>(EventNames.InviteUpdateError).unsubscribe,
|
|
148
|
-
rejectInvite: this.createEvent<{ tenant: string; }>(EventNames.RejectInvite).unsubscribe,
|
|
149
|
+
rejectInvite: this.createEvent<{ tenant: string; leave: boolean }>(EventNames.RejectInvite).unsubscribe,
|
|
149
150
|
inviteRejected: this.createEvent<{ user: string; tenant: string; }>(EventNames.InviteRejected).unsubscribe,
|
|
150
151
|
inviteRejectError: this.createEvent<{ tenant: string; }>(EventNames.InviteRejectError).unsubscribe,
|
|
151
152
|
createToken: this.createEvent<{ tenant: string; expiration: string; description: string }>(EventNames.CreateToken).unsubscribe,
|
|
@@ -405,6 +406,9 @@ export class EventHelper {
|
|
|
405
406
|
deletionError: this.createEvent<Service>(EventNames.MarketplaceItemDeletionError).publish,
|
|
406
407
|
loadItems: this.createEvent<string[]>(EventNames.LoadMarketplaceItems).publish,
|
|
407
408
|
itemsLoaded: this.createEvent<MarketplaceItem[]>(EventNames.MarketplaceItemsLoaded).publish,
|
|
409
|
+
loadSchema: this.createEvent<string>(EventNames.LoadMarketplaceSchema).publish,
|
|
410
|
+
schemaLoaded: this.createEvent<MarketplaceItem>(EventNames.MarketplaceSchemaLoaded).publish,
|
|
411
|
+
schemaLoadError: this.createEvent<string>(EventNames.MarketplaceSchemaLoadError).publish,
|
|
408
412
|
},
|
|
409
413
|
subscribe: {
|
|
410
414
|
deployItem: this.createEvent<MarketplaceService>(EventNames.DeployMarketplaceItem).subscribe,
|
|
@@ -418,6 +422,9 @@ export class EventHelper {
|
|
|
418
422
|
deletionError: this.createEvent<Service>(EventNames.MarketplaceItemDeletionError).subscribe,
|
|
419
423
|
loadItems: this.createEvent<string[]>(EventNames.LoadMarketplaceItems).subscribe,
|
|
420
424
|
itemsLoaded: this.createEvent<MarketplaceItem[]>(EventNames.MarketplaceItemsLoaded).subscribe,
|
|
425
|
+
loadSchema: this.createEvent<string>(EventNames.LoadMarketplaceSchema).subscribe,
|
|
426
|
+
schemaLoaded: this.createEvent<MarketplaceItem>(EventNames.MarketplaceSchemaLoaded).subscribe,
|
|
427
|
+
schemaLoadError: this.createEvent<string>(EventNames.MarketplaceSchemaLoadError).subscribe,
|
|
421
428
|
},
|
|
422
429
|
unsubscribe: {
|
|
423
430
|
deployItem: this.createEvent<MarketplaceService>(EventNames.DeployMarketplaceItem).unsubscribe,
|
|
@@ -431,6 +438,9 @@ export class EventHelper {
|
|
|
431
438
|
deletionError: this.createEvent<Service>(EventNames.MarketplaceItemDeletionError).unsubscribe,
|
|
432
439
|
loadItems: this.createEvent<string[]>(EventNames.LoadMarketplaceItems).unsubscribe,
|
|
433
440
|
itemsLoaded: this.createEvent<MarketplaceItem[]>(EventNames.MarketplaceItemsLoaded).unsubscribe,
|
|
441
|
+
loadSchema: this.createEvent<string>(EventNames.LoadMarketplaceSchema).unsubscribe,
|
|
442
|
+
schemaLoaded: this.createEvent<MarketplaceItem>(EventNames.MarketplaceSchemaLoaded).unsubscribe,
|
|
443
|
+
schemaLoadError: this.createEvent<string>(EventNames.MarketplaceSchemaLoadError).unsubscribe,
|
|
434
444
|
},
|
|
435
445
|
};
|
|
436
446
|
}
|
package/event-names.ts
CHANGED
|
@@ -117,6 +117,9 @@ export enum EventNames {
|
|
|
117
117
|
MarketplaceItemDeletionError = "marketplaceItemDeletionError",
|
|
118
118
|
LoadMarketplaceItems = "loadMarketplaceItems",
|
|
119
119
|
MarketplaceItemsLoaded = "marketplaceItemsLoaded",
|
|
120
|
+
LoadMarketplaceSchema = "loadMarketplaceSchema",
|
|
121
|
+
MarketplaceSchemaLoaded = "marketplaceSchemaLoaded",
|
|
122
|
+
MarketplaceSchemaLoadError = "marketplaceSchemaLoadError",
|
|
120
123
|
// * Resource
|
|
121
124
|
CreateResource = "createResource",
|
|
122
125
|
ResourceCreated = "resourceCreated",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Account, Notification } from "@kumori/aurora-interfaces";
|
|
1
|
+
import { Account, getWuiErrorCode, Notification } from "@kumori/aurora-interfaces";
|
|
2
2
|
const CREDENTIAL_ERROR_CODES = new Set([
|
|
3
3
|
"_error_retrieving_credentials_",
|
|
4
4
|
"_invalid_account_credentials_",
|
|
@@ -169,11 +169,14 @@ export const syncAccountStatusFromNotifications = (
|
|
|
169
169
|
const accountName: string = notification.data?.account;
|
|
170
170
|
if (!accountName) continue;
|
|
171
171
|
|
|
172
|
-
const
|
|
172
|
+
const accountTenant: string = notification.data?.tenant ?? "";
|
|
173
|
+
const accountKey = accountTenant ? `${accountTenant}/${accountName}` : accountName;
|
|
174
|
+
|
|
175
|
+
const account = accountsMap.get(accountKey);
|
|
173
176
|
if (!account || ACCOUNT_ERROR_STATUSES.has(account.status)) continue;
|
|
174
177
|
|
|
175
178
|
const code: string = notification.info_content?.code ?? "";
|
|
176
|
-
accountsMap.set(
|
|
179
|
+
accountsMap.set(accountKey, {
|
|
177
180
|
...account,
|
|
178
181
|
status: resolveErrorStatus(code),
|
|
179
182
|
});
|
|
@@ -194,7 +197,8 @@ export const handleAccountEvent = ({
|
|
|
194
197
|
extractCloudProviderCredentials(eventData);
|
|
195
198
|
const accountLabels: Record<string, string> = eventData.meta.labels;
|
|
196
199
|
const hasCredentials = "__axebow::managedCredentials" in accountLabels;
|
|
197
|
-
const
|
|
200
|
+
const accountKey = accountTenantId ? `${accountTenantId}/${entityId}` : entityId;
|
|
201
|
+
const existingAccount = accountsMap.get(accountKey);
|
|
198
202
|
const accountStatus = determineAccountStatus(eventData, existingAccount);
|
|
199
203
|
|
|
200
204
|
const newAccount: Account = {
|
|
@@ -288,7 +292,7 @@ export const handleAccountOperationSuccess = ({
|
|
|
288
292
|
subtype: "account-deleted",
|
|
289
293
|
date: Date.now().toString(),
|
|
290
294
|
status: "unread",
|
|
291
|
-
|
|
295
|
+
notificationType: "regular",
|
|
292
296
|
data: { account: originalData.name, tenant: originalData.tenant },
|
|
293
297
|
},
|
|
294
298
|
eventType: "deleted",
|
|
@@ -307,7 +311,7 @@ export const handleAccountOperationSuccess = ({
|
|
|
307
311
|
subtype: isCreate ? "account-created" : "account-updated",
|
|
308
312
|
date: Date.now().toString(),
|
|
309
313
|
status: "unread",
|
|
310
|
-
|
|
314
|
+
notificationType: "regular",
|
|
311
315
|
data: { account: updatedAccount.name, tenant: updatedAccount.tenant },
|
|
312
316
|
},
|
|
313
317
|
eventType: isCreate ? "created" : "updated",
|
|
@@ -322,7 +326,7 @@ export const handleAccountOperationSuccess = ({
|
|
|
322
326
|
subtype: "account-unknown",
|
|
323
327
|
date: Date.now().toString(),
|
|
324
328
|
status: "unread",
|
|
325
|
-
|
|
329
|
+
notificationType: "regular",
|
|
326
330
|
data: {},
|
|
327
331
|
},
|
|
328
332
|
eventType: null,
|
|
@@ -345,12 +349,6 @@ export const handleAccountOperationError = ({
|
|
|
345
349
|
const isCreate = action === "CREATE";
|
|
346
350
|
const isUpdate = action === "UPDATE";
|
|
347
351
|
|
|
348
|
-
const subtype = isCreate
|
|
349
|
-
? "account-creation-error"
|
|
350
|
-
: isUpdate
|
|
351
|
-
? "account-update-error"
|
|
352
|
-
: "account-deletion-error";
|
|
353
|
-
|
|
354
352
|
const eventType: "creationError" | "updateError" | "deletionError" = isCreate
|
|
355
353
|
? "creationError"
|
|
356
354
|
: isUpdate
|
|
@@ -359,9 +357,11 @@ export const handleAccountOperationError = ({
|
|
|
359
357
|
const errorCode: string = error?.error?.code ?? error?.code ?? "";
|
|
360
358
|
const errorStatus = resolveErrorStatus(errorCode);
|
|
361
359
|
|
|
360
|
+
const errSubtype = isCreate ? "account-creation-error" : isUpdate ? "account-update-error" : "account-deletion-error";
|
|
361
|
+
const errMethod = isCreate ? "createAccount" : isUpdate ? "updateAccount" : "deleteAccount";
|
|
362
362
|
const notification: Notification = {
|
|
363
363
|
type: "error",
|
|
364
|
-
subtype,
|
|
364
|
+
subtype: getWuiErrorCode("account", errMethod, errorCode) ?? errSubtype,
|
|
365
365
|
date: Date.now().toString(),
|
|
366
366
|
status: "unread",
|
|
367
367
|
info_content: {
|
|
@@ -370,7 +370,7 @@ export const handleAccountOperationError = ({
|
|
|
370
370
|
error?.error?.content ?? error?.error?.message ?? "Unknown error",
|
|
371
371
|
timestamp: error?.error?.timestamp ?? Date.now().toString(),
|
|
372
372
|
},
|
|
373
|
-
|
|
373
|
+
notificationType: "regular",
|
|
374
374
|
data: {
|
|
375
375
|
account:
|
|
376
376
|
entityName || originalData?.name || originalData?.account || "unknown",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Account, Environment, Notification } from "@kumori/aurora-interfaces";
|
|
1
|
+
import { Account, Environment, getWuiErrorCode, Notification } from "@kumori/aurora-interfaces";
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
@@ -56,7 +56,8 @@ export const handleEnvironmentEvent = ({
|
|
|
56
56
|
}: HandleEnvironmentEventParams): HandleEnvironmentEventResult => {
|
|
57
57
|
const tenantId = parentParts.tenant;
|
|
58
58
|
const accountId = parentParts.account;
|
|
59
|
-
const
|
|
59
|
+
const accountKey = tenantId ? `${tenantId}/${accountId}` : accountId;
|
|
60
|
+
const relatedAccount = accountsMap.get(accountKey);
|
|
60
61
|
let cloudProviderName = relatedAccount?.cloudProvider?.name || "";
|
|
61
62
|
|
|
62
63
|
let pendingCloudProviderUpdate: { environmentId: string; accountId: string } | null = null;
|
|
@@ -68,7 +69,8 @@ export const handleEnvironmentEvent = ({
|
|
|
68
69
|
};
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
const
|
|
72
|
+
const envKey = tenantId && accountId ? `${tenantId}/${accountId}/${entityId}` : entityId;
|
|
73
|
+
const existingEnvironment = environmentsMap.get(envKey);
|
|
72
74
|
|
|
73
75
|
const newEnvironment: Environment = {
|
|
74
76
|
id: entityId,
|
|
@@ -78,17 +80,18 @@ export const handleEnvironmentEvent = ({
|
|
|
78
80
|
logo: "",
|
|
79
81
|
services: [],
|
|
80
82
|
domains: [],
|
|
81
|
-
status:
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
83
|
+
status: (() => {
|
|
84
|
+
const existingCode = existingEnvironment?.status?.code;
|
|
85
|
+
const isPurging = existingCode === "PURGING" || existingCode === "CLEANING_ENVIRONMENT";
|
|
86
|
+
const eventCode = eventData.meta?.deleted ? "DELETING" : eventData.status?.state?.code;
|
|
87
|
+
if (isPurging && typeof eventCode === "string" && eventCode.startsWith("DELETING")) {
|
|
88
|
+
return { code: "PURGING", message: `Environment ${entityId} is being purged.`, timestamp: new Date().toISOString() };
|
|
89
|
+
}
|
|
90
|
+
if (eventData.meta?.deleted) {
|
|
91
|
+
return { code: "DELETING", message: `Environment ${entityId} is being deleted.`, timestamp: new Date().toISOString() };
|
|
92
|
+
}
|
|
93
|
+
return { code: eventData.status.state.code, message: eventData.status.state.message, timestamp: eventData.status.state.timestamp };
|
|
94
|
+
})(),
|
|
92
95
|
highAvaliable: eventData.spec.highlyAvailable || false,
|
|
93
96
|
usage: {
|
|
94
97
|
current: {
|
|
@@ -184,7 +187,7 @@ export const handleEnvironmentOperationSuccess = ({
|
|
|
184
187
|
subtype: "environment-cleaned",
|
|
185
188
|
date: Date.now().toString(),
|
|
186
189
|
status: "unread",
|
|
187
|
-
|
|
190
|
+
notificationType: "regular",
|
|
188
191
|
data: {
|
|
189
192
|
environment: envData.name,
|
|
190
193
|
account: envData.account,
|
|
@@ -240,23 +243,17 @@ export const handleEnvironmentOperationError = ({
|
|
|
240
243
|
originalData,
|
|
241
244
|
error,
|
|
242
245
|
}: HandleEnvironmentOperationErrorParams): HandleEnvironmentOperationErrorResult => {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
eventType = "creationError";
|
|
249
|
-
} else if (action === "UPDATE") {
|
|
250
|
-
subtype = "environment-update-error";
|
|
251
|
-
eventType = "updateError";
|
|
252
|
-
} else {
|
|
253
|
-
subtype = "environment-deletion-error";
|
|
254
|
-
eventType = "deletionError";
|
|
255
|
-
}
|
|
246
|
+
const eventType: "creationError" | "updateError" | "deletionError" = action === "CREATE"
|
|
247
|
+
? "creationError"
|
|
248
|
+
: action === "UPDATE"
|
|
249
|
+
? "updateError"
|
|
250
|
+
: "deletionError";
|
|
256
251
|
|
|
252
|
+
const envErrSubtype = action === "CREATE" ? "environment-creation-error" : action === "UPDATE" ? "environment-update-error" : "environment-deletion-error";
|
|
253
|
+
const envErrMethod = action === "CREATE" ? "createEnvironment" : action === "UPDATE" ? "updateEnvironment" : "deleteEnvironment";
|
|
257
254
|
const envErrorNotification: Notification = {
|
|
258
255
|
type: "error",
|
|
259
|
-
subtype,
|
|
256
|
+
subtype: getWuiErrorCode("environment", envErrMethod, error?.error?.code || "UNKNOWN_ERROR") ?? envErrSubtype,
|
|
260
257
|
date: Date.now().toString(),
|
|
261
258
|
status: "unread",
|
|
262
259
|
info_content: {
|
|
@@ -264,7 +261,7 @@ export const handleEnvironmentOperationError = ({
|
|
|
264
261
|
message: error?.error?.content || error?.error?.message || "Unknown error",
|
|
265
262
|
timestamp: error?.error?.timestamp || "",
|
|
266
263
|
},
|
|
267
|
-
|
|
264
|
+
notificationType: "regular",
|
|
268
265
|
data: {
|
|
269
266
|
environment: originalData.name,
|
|
270
267
|
account: originalData.account,
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export const processMarketplaceSchemaResponse = (rawEntry: any, nameKey: string) => {
|
|
2
|
+
const roles: string[] = rawEntry.roles || [];
|
|
3
|
+
const roleName = roles.join(", ");
|
|
4
|
+
const schemas = rawEntry.schemas || [];
|
|
5
|
+
|
|
6
|
+
const processSchema = (schema: any) => {
|
|
7
|
+
const parameters: any[] = [];
|
|
8
|
+
const resources: any[] = [];
|
|
9
|
+
const channels: any[] = [];
|
|
10
|
+
|
|
11
|
+
const configProps = schema.properties?.config?.properties;
|
|
12
|
+
if (configProps) {
|
|
13
|
+
Object.entries(configProps).forEach(([key, value]: [string, any]) => {
|
|
14
|
+
parameters.push({
|
|
15
|
+
name: key,
|
|
16
|
+
type: value.type || "string",
|
|
17
|
+
required: schema.properties?.config?.required?.includes(key) || false,
|
|
18
|
+
defaultValue: value.default ?? value.enum?.[0] ?? value.const ?? undefined,
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const volumeTypes = ["Registered", "NonReplicated", "Persisted", "Volatile", "Ephemeral", "Persistent"];
|
|
24
|
+
const isVolumeResource = (v: any): boolean => {
|
|
25
|
+
if (!v.oneOf) return false;
|
|
26
|
+
return v.oneOf.some((o: any) => {
|
|
27
|
+
if (o.oneOf) return o.oneOf.some((n: any) => volumeTypes.includes(n?.properties?.$kdsl?.const?.NamedType?.Name));
|
|
28
|
+
return volumeTypes.includes(o?.properties?.$kdsl?.const?.NamedType?.Name);
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const resourceProps = schema.properties?.resource;
|
|
33
|
+
if (resourceProps?.properties) {
|
|
34
|
+
Object.entries(resourceProps.properties).forEach(([key, value]: [string, any]) => {
|
|
35
|
+
if (isVolumeResource(value)) {
|
|
36
|
+
resources.push({
|
|
37
|
+
name: key,
|
|
38
|
+
type: "volume",
|
|
39
|
+
required: resourceProps.required?.includes(key) || false,
|
|
40
|
+
});
|
|
41
|
+
} else {
|
|
42
|
+
const typeName = value.properties?.["$kdsl"]?.const?.NamedType?.Name;
|
|
43
|
+
const resourceType = typeName ? typeName.toLowerCase() : Object.keys(value.properties || {})[0];
|
|
44
|
+
const inner = value.properties?.[resourceType] || value.properties?.inner;
|
|
45
|
+
const defaultValue =
|
|
46
|
+
inner?.default !== undefined
|
|
47
|
+
? String(inner.default)
|
|
48
|
+
: inner?.enum?.[0]
|
|
49
|
+
? String(inner.enum[0])
|
|
50
|
+
: undefined;
|
|
51
|
+
resources.push({
|
|
52
|
+
name: key,
|
|
53
|
+
type: resourceType || "string",
|
|
54
|
+
required: resourceProps.required?.includes(key) || false,
|
|
55
|
+
defaultValue,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const srvProps = schema.properties?.srv?.properties;
|
|
62
|
+
if (srvProps) {
|
|
63
|
+
(["client", "server", "duplex"] as const).forEach((channelType) => {
|
|
64
|
+
if (srvProps[channelType]?.properties) {
|
|
65
|
+
Object.entries(srvProps[channelType].properties).forEach(([channelName, channelData]: [string, any]) => {
|
|
66
|
+
const port = channelData.properties?.port?.const || channelData.properties?.port?.enum?.[0];
|
|
67
|
+
const protocol = channelData.properties?.protocol?.const || channelData.properties?.protocol?.enum?.[0];
|
|
68
|
+
channels.push({
|
|
69
|
+
name: channelName,
|
|
70
|
+
type: channelType,
|
|
71
|
+
resource: port && protocol ? { type: "port", value: `${protocol}:${port}` } : undefined,
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { parameters, resources, channels };
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
if (schemas.length > 0 && schemas[0].oneOf) {
|
|
82
|
+
const processedVariants: any[] = [];
|
|
83
|
+
for (const schemaRef of schemas[0].oneOf) {
|
|
84
|
+
const refKey = schemaRef.$ref?.replace("#/$defs/", "");
|
|
85
|
+
const actualSchema = schemas[0].$defs?.[refKey];
|
|
86
|
+
if (actualSchema) {
|
|
87
|
+
const result = processSchema(actualSchema);
|
|
88
|
+
processedVariants.push({ name: nameKey, roleName, roles, variant: refKey, ...result });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
parameters: processedVariants[0]?.parameters || [],
|
|
93
|
+
resources: processedVariants[0]?.resources || [],
|
|
94
|
+
channels: processedVariants[0]?.channels || [],
|
|
95
|
+
name: nameKey,
|
|
96
|
+
roleName,
|
|
97
|
+
roles,
|
|
98
|
+
hasVariants: true,
|
|
99
|
+
variants: processedVariants,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (schemas[0]) {
|
|
104
|
+
const result = processSchema(schemas[0]);
|
|
105
|
+
return { ...result, name: nameKey, roleName, roles, hasVariants: false };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return { parameters: [], resources: [], channels: [], name: nameKey, roleName, roles, hasVariants: false };
|
|
109
|
+
};
|
|
@@ -239,7 +239,9 @@ export const handleResourceEvent = ({
|
|
|
239
239
|
if (existingIndex !== -1) {
|
|
240
240
|
existingResource = tenant.resources[existingIndex];
|
|
241
241
|
publishUpdate = true;
|
|
242
|
-
tenant.resources[existingIndex] =
|
|
242
|
+
tenant.resources[existingIndex] = existingResource.deleting
|
|
243
|
+
? { ...resource, deleting: true }
|
|
244
|
+
: resource;
|
|
243
245
|
} else {
|
|
244
246
|
tenant.resources.push(resource);
|
|
245
247
|
}
|
|
@@ -376,6 +378,22 @@ export const handleVolumeEvent = (
|
|
|
376
378
|
hasRequestingServices: false
|
|
377
379
|
});
|
|
378
380
|
};
|
|
381
|
+
export const setResourceDeleting = (
|
|
382
|
+
tenantsMap: Map<string, any>,
|
|
383
|
+
tenantName: string,
|
|
384
|
+
resourceName: string,
|
|
385
|
+
resourceType: string,
|
|
386
|
+
) => {
|
|
387
|
+
const tenant = tenantsMap.get(tenantName);
|
|
388
|
+
if (!tenant) return;
|
|
389
|
+
const index = tenant.resources.findIndex(
|
|
390
|
+
(res: Resource) => res.name === resourceName && res.type === resourceType,
|
|
391
|
+
);
|
|
392
|
+
if (index === -1) return;
|
|
393
|
+
tenant.resources[index] = { ...tenant.resources[index], deleting: true };
|
|
394
|
+
tenantsMap.set(tenantName, tenant);
|
|
395
|
+
};
|
|
396
|
+
|
|
379
397
|
export const processResourceResult = (
|
|
380
398
|
result: any,
|
|
381
399
|
tenantsMap: Map<string, any>,
|
|
@@ -159,12 +159,55 @@ const processRolesAndInstances = (
|
|
|
159
159
|
return { roles, instances, usedCpu, usedMemory };
|
|
160
160
|
};
|
|
161
161
|
|
|
162
|
+
const getContainerError = (
|
|
163
|
+
revisionStatus: any,
|
|
164
|
+
): { code: string; message: string; timestamp: string } | null => {
|
|
165
|
+
for (const roleData of Object.values<any>(revisionStatus?.runtime?.roles ?? {})) {
|
|
166
|
+
for (const instanceData of Object.values<any>(roleData.instances ?? {})) {
|
|
167
|
+
for (const container of Object.values<any>(instanceData.containers ?? {})) {
|
|
168
|
+
if (!container.status.ready) {
|
|
169
|
+
return {
|
|
170
|
+
code: container.status.status.waiting?.reason ?? "ContainerNotReady",
|
|
171
|
+
message: container.status.status.waiting?.message ?? "One or more containers are not ready for unknown reasons.",
|
|
172
|
+
timestamp: Date.now().toString(),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return null;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const handleRevisionStatus = (revisionStatus: any) => {
|
|
182
|
+
if (!revisionStatus?.runtime) {
|
|
183
|
+
return revisionStatus?.state;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
for (const roleData of Object.values<any>(revisionStatus.runtime.roles)) {
|
|
187
|
+
for (const instanceData of Object.values<any>(roleData.instances)) {
|
|
188
|
+
for (const container of Object.values<any>(instanceData.containers ?? {})) {
|
|
189
|
+
if (!container.status.ready) {
|
|
190
|
+
return {
|
|
191
|
+
code: container.status.status.waiting?.reason ?? "ContainerNotReady",
|
|
192
|
+
message: container.status.status.waiting?.message ?? "One or more containers are not ready for unknown reasons.",
|
|
193
|
+
timestamp: Date.now().toString(),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return revisionStatus?.state;
|
|
201
|
+
};
|
|
202
|
+
|
|
162
203
|
const createRevision = (
|
|
163
204
|
entityId: string,
|
|
164
205
|
eventData: any,
|
|
165
206
|
usedCpu: number,
|
|
166
207
|
usedMemory: number,
|
|
167
208
|
): Revision => {
|
|
209
|
+
const containerError = eventData.status?.error ? null : getContainerError(eventData.status);
|
|
210
|
+
|
|
168
211
|
return {
|
|
169
212
|
id: entityId,
|
|
170
213
|
schema: {},
|
|
@@ -181,7 +224,7 @@ const createRevision = (
|
|
|
181
224
|
cpu: { max: eventData.spec.intensives?.vcpu / 1000 || 0, min: 0 },
|
|
182
225
|
memory: { max: eventData.spec.intensives?.ram / 1000 || 0, min: 0 },
|
|
183
226
|
storage: {
|
|
184
|
-
max: eventData.spec.intensives?.
|
|
227
|
+
max: (eventData.spec.intensives?.volatile_disk / 1000 || 0) + (eventData.spec.intensives?.nrpersistent_disk / 1000 || 0) + (eventData.spec.intensives?.persistent_disk / 1000 || 0),
|
|
185
228
|
min: 0,
|
|
186
229
|
},
|
|
187
230
|
volatileStorage: {
|
|
@@ -199,14 +242,13 @@ const createRevision = (
|
|
|
199
242
|
},
|
|
200
243
|
cost: 0,
|
|
201
244
|
},
|
|
202
|
-
status: eventData.status
|
|
203
|
-
errorCode: eventData.status?.error ? eventData.status.error.code : "",
|
|
204
|
-
errorMsg: eventData.status?.error ? eventData.status.error.message : "",
|
|
245
|
+
status: handleRevisionStatus(eventData.status),
|
|
246
|
+
errorCode: eventData.status?.error ? eventData.status.error.code : containerError?.code === "CrashLoopBackOff" ? containerError.code : "",
|
|
247
|
+
errorMsg: eventData.status?.error ? eventData.status.error.message : containerError?.code === "CrashLoopBackOff" ? containerError.message : "",
|
|
205
248
|
createdAt:
|
|
206
249
|
(eventData.status && eventData.status.runtime?.status?.createdAt) || "",
|
|
207
250
|
};
|
|
208
251
|
};
|
|
209
|
-
|
|
210
252
|
const updateServiceWithRevision = (
|
|
211
253
|
existingService: Service,
|
|
212
254
|
entityId: string,
|
|
@@ -239,13 +281,21 @@ const updateServiceWithRevision = (
|
|
|
239
281
|
String(rev.id) === String(entityId) ? newRevision : rev,
|
|
240
282
|
);
|
|
241
283
|
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
284
|
+
const isCurrentRevision =
|
|
285
|
+
String(existingService.currentRevision) === String(entityId);
|
|
286
|
+
|
|
287
|
+
const updatedService: Service = isCurrentRevision
|
|
288
|
+
? {
|
|
289
|
+
...existingService,
|
|
290
|
+
revisions: updatedRevisions,
|
|
291
|
+
role: roles.length > 0 ? roles : existingService.role,
|
|
292
|
+
usage: newRevision.usage,
|
|
293
|
+
startedAt: newRevision.createdAt || existingService.startedAt,
|
|
294
|
+
}
|
|
295
|
+
: {
|
|
296
|
+
...existingService,
|
|
297
|
+
revisions: updatedRevisions,
|
|
298
|
+
};
|
|
249
299
|
|
|
250
300
|
const deploymentErrorEvent = eventData.status?.error ? updatedService : null;
|
|
251
301
|
|
|
@@ -262,7 +312,7 @@ const updateEnvironmentConsumption = (
|
|
|
262
312
|
|
|
263
313
|
servicesMap.forEach((service, svcId) => {
|
|
264
314
|
if (service.environment === serviceEnvironmentId) {
|
|
265
|
-
const currentRevisionKey =
|
|
315
|
+
const currentRevisionKey = `/tenant/${svcId.split('/')[0]}/service/${svcId.split('/')[1]}/revision/${service.currentRevision}`;
|
|
266
316
|
const currentRevision = revisionsMap.get(currentRevisionKey);
|
|
267
317
|
if (currentRevision) {
|
|
268
318
|
totalEnvCpu += currentRevision.usage.limit.cpu.max || 0;
|
|
@@ -300,7 +350,7 @@ const updateAccountConsumption = (
|
|
|
300
350
|
|
|
301
351
|
servicesMap.forEach((service, svcId) => {
|
|
302
352
|
if (service.account === serviceAccountId) {
|
|
303
|
-
const currentRevisionKey =
|
|
353
|
+
const currentRevisionKey = `/tenant/${svcId.split('/')[0]}/service/${svcId.split('/')[1]}/revision/${service.currentRevision}`;
|
|
304
354
|
const currentRevision = revisionsMap.get(currentRevisionKey);
|
|
305
355
|
if (currentRevision) {
|
|
306
356
|
totalAccountCpu += currentRevision.usage.limit.cpu.max || 0;
|
|
@@ -338,7 +388,7 @@ export const handleRevisionEvent = ({
|
|
|
338
388
|
accountsMap,
|
|
339
389
|
}: HandleRevisionEventParams): HandleRevisionEventResult => {
|
|
340
390
|
const serviceId = `${parentParts.tenant}/${parentParts.service}`;
|
|
341
|
-
const revisionKey = `${
|
|
391
|
+
const revisionKey = `${eventData.id.parent.name}/revision/${entityId}`;
|
|
342
392
|
const { roles, instances, usedCpu, usedMemory } = processRolesAndInstances(
|
|
343
393
|
eventData,
|
|
344
394
|
serviceId,
|
|
@@ -646,7 +696,6 @@ export const processRevisionData = (
|
|
|
646
696
|
: {};
|
|
647
697
|
const schema =
|
|
648
698
|
schemaResult && "parameters" in schemaResult ? schemaResult : {};
|
|
649
|
-
|
|
650
699
|
if (
|
|
651
700
|
revisionsMap &&
|
|
652
701
|
serviceId &&
|
|
@@ -654,7 +703,8 @@ export const processRevisionData = (
|
|
|
654
703
|
"parameters" in schema
|
|
655
704
|
) {
|
|
656
705
|
const revisionId: string = revisionData.revision;
|
|
657
|
-
const
|
|
706
|
+
const [tenantId, serviceName] = serviceId.split('/');
|
|
707
|
+
const revisionKey = `/tenant/${tenantId}/service/${serviceName}/revision/${revisionId}`;
|
|
658
708
|
const existing = revisionsMap.get(revisionKey);
|
|
659
709
|
if (existing) {
|
|
660
710
|
revisionsMap.set(revisionKey, { ...existing, schema });
|
|
@@ -675,7 +725,7 @@ export const processRevisionData = (
|
|
|
675
725
|
cpu: { max: revisionData.intensives?.vcpu / 1000 || 0, min: 0 },
|
|
676
726
|
memory: { max: revisionData.intensives?.ram / 1000 || 0, min: 0 },
|
|
677
727
|
storage: {
|
|
678
|
-
max: revisionData.intensives?.
|
|
728
|
+
max: (revisionData.intensives?.volatile_disk / 1000 || 0) + (revisionData.intensives?.nrpersistent_disk / 1000 || 0) + (revisionData.intensives?.persistent_disk / 1000 || 0),
|
|
679
729
|
min: 0,
|
|
680
730
|
},
|
|
681
731
|
volatileStorage: {
|