@kumori/aurora-backend-handler 1.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/.gitlab-ci.yml +57 -0
- package/README.md +352 -0
- package/api/account-api-service.ts +1092 -0
- package/api/deploy-service-helper.ts +1611 -0
- package/api/environment-api-service.ts +542 -0
- package/api/marketplace-api-service.ts +1031 -0
- package/api/organizations-api-service.ts +0 -0
- package/api/planProvider-api-service.ts +24 -0
- package/api/reporting-api-service.ts +35 -0
- package/api/resources-api-service.ts +821 -0
- package/api/service-api-service.ts +796 -0
- package/api/tenant-api-service.ts +1260 -0
- package/api/user-api-service.ts +1161 -0
- package/backend-handler.ts +1127 -0
- package/environment.ts +7 -0
- package/event-helper.ts +577 -0
- package/event-names.ts +152 -0
- package/helpers/account-helper.ts +331 -0
- package/helpers/environment-helper.ts +289 -0
- package/helpers/link-helper.ts +114 -0
- package/helpers/plan-helper.ts +104 -0
- package/helpers/registry-helper.ts +134 -0
- package/helpers/resource-helper.ts +387 -0
- package/helpers/revision-helper.ts +899 -0
- package/helpers/service-helper.ts +627 -0
- package/helpers/tenant-helper.ts +191 -0
- package/helpers/token-helper.ts +107 -0
- package/helpers/user-helper.ts +140 -0
- package/jest.config.ts +40 -0
- package/jest.setup.js +4 -0
- package/package.json +50 -0
- package/test/backend-handler.test.ts +792 -0
- package/test/deploy-service-helper.test.ts +518 -0
- package/test/event-helper.test.ts +152 -0
- package/tsconfig.json +26 -0
- package/utils/utils.ts +78 -0
- package/websocket-manager.ts +1833 -0
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
import { environment } from "../environment";
|
|
2
|
+
import { deployServiceHelper } from "./deploy-service-helper";
|
|
3
|
+
import { eventHelper } from "../backend-handler";
|
|
4
|
+
import {
|
|
5
|
+
initializeGlobalWebSocketClient,
|
|
6
|
+
getWebSocketStatus,
|
|
7
|
+
makeGlobalWebSocketRequest,
|
|
8
|
+
} from "../websocket-manager";
|
|
9
|
+
import { Link, Notification, Service } from "@hestekumori/aurora-interfaces";
|
|
10
|
+
let pendingLinks = new Map<string, Link[]>();
|
|
11
|
+
/**
|
|
12
|
+
* Function to deploy a service
|
|
13
|
+
* @param data Service object with the data of the service
|
|
14
|
+
*/
|
|
15
|
+
export const deployService = async (data: Service, token: string) => {
|
|
16
|
+
try {
|
|
17
|
+
const url = new URL(
|
|
18
|
+
`${environment.apiServer.baseUrl}/api/${environment.apiServer.apiVersion}/tenant/${data.tenant}/service/${data.name}`
|
|
19
|
+
);
|
|
20
|
+
url.searchParams.append("dryrun", "false");
|
|
21
|
+
url.searchParams.append("accept", "true");
|
|
22
|
+
url.searchParams.append("wait", "30000");
|
|
23
|
+
url.searchParams.append("validate", "true");
|
|
24
|
+
if(data.dsl){
|
|
25
|
+
url.searchParams.append("dsl", "true");
|
|
26
|
+
}
|
|
27
|
+
if (data.serviceData) {
|
|
28
|
+
const formData = new FormData();
|
|
29
|
+
formData.append("bundle", data.serviceData);
|
|
30
|
+
formData.append(
|
|
31
|
+
"meta",
|
|
32
|
+
JSON.stringify({
|
|
33
|
+
targetAccount: data.account,
|
|
34
|
+
targetEnvironment: data.environment,
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
formData.append("labels", JSON.stringify({ project: data.project }));
|
|
38
|
+
formData.append("comment", " ");
|
|
39
|
+
const response = await fetch(url.toString(), {
|
|
40
|
+
method: "POST",
|
|
41
|
+
body: formData,
|
|
42
|
+
});
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const jsonResponse = await response.json();
|
|
48
|
+
|
|
49
|
+
const isTimeout = jsonResponse?.events?.some(
|
|
50
|
+
(event: any) => event.content === "_timeout_"
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (isTimeout) {
|
|
54
|
+
console.error("Timeout en la petición:", {
|
|
55
|
+
isOk: false,
|
|
56
|
+
code: "TIMEOUT",
|
|
57
|
+
error: "_timeout_",
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
const formData = await deployServiceHelper(data);
|
|
62
|
+
if (data.download) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const response = await fetch(url.toString(), {
|
|
66
|
+
method: "POST",
|
|
67
|
+
body: formData,
|
|
68
|
+
});
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
const jsonResponse = await response.json();
|
|
71
|
+
data.error = {
|
|
72
|
+
code: jsonResponse.code,
|
|
73
|
+
message: jsonResponse.content,
|
|
74
|
+
timestamp: new Date().toISOString(),
|
|
75
|
+
};
|
|
76
|
+
eventHelper.service.publish.deploymentError(data);
|
|
77
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const jsonResponse = await response.json();
|
|
81
|
+
|
|
82
|
+
const isTimeout = jsonResponse?.events?.some(
|
|
83
|
+
(event: any) => event.content === "_timeout_"
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (isTimeout) {
|
|
87
|
+
console.error("Timeout en la petición:", {
|
|
88
|
+
isOk: false,
|
|
89
|
+
code: "TIMEOUT",
|
|
90
|
+
error: "_timeout_",
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
if (data.links.length > 0) {
|
|
94
|
+
pendingLinks.set(data.name, data.links);
|
|
95
|
+
linkPendingServices(data, token);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.error("Error en la petición de despliegue de servicio:", err);
|
|
101
|
+
// data.error = {
|
|
102
|
+
// code: (err as any).code,
|
|
103
|
+
// message: (err as any).content,
|
|
104
|
+
// timestamp: new Date().toISOString(),
|
|
105
|
+
// }
|
|
106
|
+
// eventHelper.service.publish.deploymentError(data);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
export const redeployService = async (data: Service) => {
|
|
110
|
+
try {
|
|
111
|
+
const formData = await deployServiceHelper(data);
|
|
112
|
+
const url = new URL(
|
|
113
|
+
`${environment.apiServer.baseUrl}/api/${environment.apiServer.apiVersion}/tenant/${data.tenant}/service/${data.name}/revision/${data.currentRevision}`
|
|
114
|
+
);
|
|
115
|
+
url.searchParams.append("dryrun", "false");
|
|
116
|
+
url.searchParams.append("accept", "true");
|
|
117
|
+
url.searchParams.append("wait", "30000");
|
|
118
|
+
url.searchParams.append("validate", "true");
|
|
119
|
+
|
|
120
|
+
const response = await fetch(url.toString(), {
|
|
121
|
+
method: "POST",
|
|
122
|
+
body: formData,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (!response.ok) {
|
|
126
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const jsonResponse = await response.json();
|
|
130
|
+
|
|
131
|
+
const isTimeout = jsonResponse?.events?.some(
|
|
132
|
+
(event: any) => event.content === "_timeout_"
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
if (isTimeout) {
|
|
136
|
+
console.error("Timeout en la petición:", {
|
|
137
|
+
isOk: false,
|
|
138
|
+
code: "TIMEOUT",
|
|
139
|
+
error: "_timeout_",
|
|
140
|
+
});
|
|
141
|
+
} else {
|
|
142
|
+
eventHelper.service.publish.deployed(data);
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
console.error("Error en la petición de redepliegue de servicio:", err);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
export const deleteService = async (data: Service, security: string) => {
|
|
149
|
+
try {
|
|
150
|
+
await initializeGlobalWebSocketClient(security);
|
|
151
|
+
const status = getWebSocketStatus();
|
|
152
|
+
|
|
153
|
+
const deleteBody = {
|
|
154
|
+
tenant: data.tenant,
|
|
155
|
+
service: data.name,
|
|
156
|
+
wait: 0,
|
|
157
|
+
remove_links: true,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const response = await makeGlobalWebSocketRequest(
|
|
161
|
+
"service:delete_service",
|
|
162
|
+
deleteBody,
|
|
163
|
+
30000,
|
|
164
|
+
"DELETE",
|
|
165
|
+
data.name
|
|
166
|
+
);
|
|
167
|
+
return response;
|
|
168
|
+
} catch (err) {
|
|
169
|
+
console.error("Error in service deletion WebSocket request:", err);
|
|
170
|
+
eventHelper.service.publish.deletionError(data);
|
|
171
|
+
throw err;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export const restartService = async (data: Service, security: string) => {
|
|
176
|
+
try {
|
|
177
|
+
await initializeGlobalWebSocketClient(security);
|
|
178
|
+
const status = getWebSocketStatus();
|
|
179
|
+
|
|
180
|
+
const restartBody = {
|
|
181
|
+
tenant: data.tenant,
|
|
182
|
+
service: data.name,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const response = await makeGlobalWebSocketRequest(
|
|
186
|
+
"service:restart",
|
|
187
|
+
restartBody,
|
|
188
|
+
30000,
|
|
189
|
+
"RESTART",
|
|
190
|
+
data.name
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const updatedService: Service = {
|
|
194
|
+
...data,
|
|
195
|
+
status: {
|
|
196
|
+
code: "PENDING",
|
|
197
|
+
message: "",
|
|
198
|
+
timestamp: "",
|
|
199
|
+
args: [],
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
eventHelper.service.publish.restarted(updatedService);
|
|
203
|
+
return response;
|
|
204
|
+
} catch (err) {
|
|
205
|
+
console.error("Error in service restart WebSocket request:", err);
|
|
206
|
+
eventHelper.service.publish.restartError(data);
|
|
207
|
+
throw err;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
const generateLinkBody = (data: Service, link: Link) => {
|
|
211
|
+
const originInClient = data.clientChannels.find(
|
|
212
|
+
(channel) =>
|
|
213
|
+
channel.name === link.originChannel || channel.from === link.originChannel
|
|
214
|
+
);
|
|
215
|
+
const originInServer = data.serverChannels.find(
|
|
216
|
+
(channel) =>
|
|
217
|
+
channel.name === link.originChannel || channel.from === link.originChannel
|
|
218
|
+
);
|
|
219
|
+
const originInDuplex = data.duplexChannels.find(
|
|
220
|
+
(channel) =>
|
|
221
|
+
channel.name === link.originChannel || channel.from === link.originChannel
|
|
222
|
+
);
|
|
223
|
+
const targetInClient = data.clientChannels.find(
|
|
224
|
+
(channel) =>
|
|
225
|
+
channel.name === link.targetChannel || channel.from === link.targetChannel
|
|
226
|
+
);
|
|
227
|
+
const targetInServer = data.serverChannels.find(
|
|
228
|
+
(channel) =>
|
|
229
|
+
channel.name === link.targetChannel || channel.from === link.targetChannel
|
|
230
|
+
);
|
|
231
|
+
const targetInDuplex = data.duplexChannels.find(
|
|
232
|
+
(channel) =>
|
|
233
|
+
channel.name === link.targetChannel || channel.from === link.targetChannel
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
let linkBody;
|
|
237
|
+
if (originInClient) {
|
|
238
|
+
linkBody = {
|
|
239
|
+
client_tenant: data.tenant,
|
|
240
|
+
client_service: data.name,
|
|
241
|
+
client_channel: link.originChannel,
|
|
242
|
+
server_tenant: data.tenant,
|
|
243
|
+
server_service: link.target,
|
|
244
|
+
server_channel: link.targetChannel,
|
|
245
|
+
};
|
|
246
|
+
} else if (targetInClient) {
|
|
247
|
+
linkBody = {
|
|
248
|
+
client_tenant: data.tenant,
|
|
249
|
+
client_service: data.name,
|
|
250
|
+
client_channel: link.targetChannel,
|
|
251
|
+
server_tenant: data.tenant,
|
|
252
|
+
server_service: link.origin,
|
|
253
|
+
server_channel: link.originChannel,
|
|
254
|
+
};
|
|
255
|
+
} else if (originInServer) {
|
|
256
|
+
linkBody = {
|
|
257
|
+
client_tenant: data.tenant,
|
|
258
|
+
client_service: link.target,
|
|
259
|
+
client_channel: link.targetChannel,
|
|
260
|
+
server_tenant: data.tenant,
|
|
261
|
+
server_service: data.name,
|
|
262
|
+
server_channel: link.originChannel,
|
|
263
|
+
};
|
|
264
|
+
} else if (targetInServer) {
|
|
265
|
+
linkBody = {
|
|
266
|
+
client_tenant: data.tenant,
|
|
267
|
+
client_service: link.origin,
|
|
268
|
+
client_channel: link.originChannel,
|
|
269
|
+
server_tenant: data.tenant,
|
|
270
|
+
server_service: data.name,
|
|
271
|
+
server_channel: link.targetChannel,
|
|
272
|
+
};
|
|
273
|
+
} else if (originInDuplex) {
|
|
274
|
+
linkBody = {
|
|
275
|
+
client_tenant: data.tenant,
|
|
276
|
+
client_service: link.target,
|
|
277
|
+
client_channel: link.targetChannel,
|
|
278
|
+
server_tenant: data.tenant,
|
|
279
|
+
server_service: data.name,
|
|
280
|
+
server_channel: link.originChannel,
|
|
281
|
+
};
|
|
282
|
+
} else if (targetInDuplex) {
|
|
283
|
+
linkBody = {
|
|
284
|
+
client_tenant: data.tenant,
|
|
285
|
+
client_service: link.origin,
|
|
286
|
+
client_channel: link.originChannel,
|
|
287
|
+
server_tenant: data.tenant,
|
|
288
|
+
server_service: data.name,
|
|
289
|
+
server_channel: link.targetChannel,
|
|
290
|
+
};
|
|
291
|
+
} else {
|
|
292
|
+
console.warn(
|
|
293
|
+
`No se encontraron canales para el enlace: origin=${link.origin}, target=${link.target}`
|
|
294
|
+
);
|
|
295
|
+
linkBody = {
|
|
296
|
+
client_tenant: data.tenant,
|
|
297
|
+
client_service: link.origin,
|
|
298
|
+
client_channel: link.originChannel,
|
|
299
|
+
server_tenant: data.tenant,
|
|
300
|
+
server_service: link.target,
|
|
301
|
+
server_channel: link.targetChannel,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
return linkBody;
|
|
305
|
+
};
|
|
306
|
+
export const linkPendingServices = async (service: Service, token: string) => {
|
|
307
|
+
const links = pendingLinks.get(service.name);
|
|
308
|
+
if (links) {
|
|
309
|
+
await Promise.all(
|
|
310
|
+
links.map(async (link) => {
|
|
311
|
+
try {
|
|
312
|
+
await initializeGlobalWebSocketClient(token);
|
|
313
|
+
const status = getWebSocketStatus();
|
|
314
|
+
const linkBody = generateLinkBody(service, link);
|
|
315
|
+
|
|
316
|
+
const linkResponse = await makeGlobalWebSocketRequest(
|
|
317
|
+
"link:link_service",
|
|
318
|
+
linkBody,
|
|
319
|
+
30000,
|
|
320
|
+
"LINK",
|
|
321
|
+
service.name
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
const notification: Notification = {
|
|
325
|
+
type: "success",
|
|
326
|
+
subtype: "service-linked",
|
|
327
|
+
date: Date.now().toString(),
|
|
328
|
+
status: "unread",
|
|
329
|
+
callToAction: false,
|
|
330
|
+
data: {
|
|
331
|
+
service: service.name,
|
|
332
|
+
tenant: service.tenant,
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
eventHelper.notification.publish.creation(notification);
|
|
337
|
+
} catch (linkErr) {
|
|
338
|
+
const notification: Notification = {
|
|
339
|
+
type: "error",
|
|
340
|
+
subtype: "error-service-link",
|
|
341
|
+
date: Date.now().toString(),
|
|
342
|
+
info_content: {
|
|
343
|
+
code: (linkErr as any).error.code,
|
|
344
|
+
message: (linkErr as any).error.content,
|
|
345
|
+
},
|
|
346
|
+
status: "unread",
|
|
347
|
+
callToAction: false,
|
|
348
|
+
data: {
|
|
349
|
+
service: service.name,
|
|
350
|
+
tenant: service.tenant,
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
eventHelper.notification.publish.creation(notification);
|
|
354
|
+
}
|
|
355
|
+
})
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
export const requestRevisionData = async (service: Service, token: string) => {
|
|
360
|
+
try {
|
|
361
|
+
await initializeGlobalWebSocketClient(token);
|
|
362
|
+
const status = getWebSocketStatus();
|
|
363
|
+
|
|
364
|
+
const revisionBody = {
|
|
365
|
+
tenant: service.tenant,
|
|
366
|
+
service: service.name,
|
|
367
|
+
revision: "latest",
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const response = await makeGlobalWebSocketRequest(
|
|
371
|
+
"revision:revision_info",
|
|
372
|
+
revisionBody,
|
|
373
|
+
30000,
|
|
374
|
+
"GET_REVISION",
|
|
375
|
+
service.name,
|
|
376
|
+
"service",
|
|
377
|
+
service
|
|
378
|
+
);
|
|
379
|
+
return response;
|
|
380
|
+
} catch (err) {
|
|
381
|
+
console.error("Error in service deletion WebSocket request:", err);
|
|
382
|
+
throw err;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
export const updateService = async (
|
|
386
|
+
data: Service,
|
|
387
|
+
token: string,
|
|
388
|
+
scale?: number
|
|
389
|
+
) => {
|
|
390
|
+
try {
|
|
391
|
+
await initializeGlobalWebSocketClient(token);
|
|
392
|
+
// await unlinkServices(data, token);
|
|
393
|
+
// const newLinks: Link[] = data.links.filter((link) => !link.delete);
|
|
394
|
+
// const newLinksToCreate = newLinks.filter((link) => link.delete === false);
|
|
395
|
+
// console.log("DEBUG - newLinks", newLinksToCreate);
|
|
396
|
+
// if (newLinksToCreate.length > 0) {
|
|
397
|
+
// const serviceWithNewLinks: Service = {
|
|
398
|
+
// ...data,
|
|
399
|
+
// links: newLinksToCreate,
|
|
400
|
+
// };
|
|
401
|
+
// pendingLinks.set(data.name, newLinksToCreate);
|
|
402
|
+
// await linkPendingServices(serviceWithNewLinks, token);
|
|
403
|
+
// }
|
|
404
|
+
|
|
405
|
+
const parameterObject: Record<string, any> = {};
|
|
406
|
+
if (data.parameters && data.parameters.length > 0) {
|
|
407
|
+
data.parameters.forEach((param) => {
|
|
408
|
+
const key = param.configKey || param.name;
|
|
409
|
+
const paramType = param.type?.toLowerCase();
|
|
410
|
+
if (paramType === "number" || paramType === "integer") {
|
|
411
|
+
parameterObject[key] = Number(param.value) || 0;
|
|
412
|
+
} else if (paramType === "boolean") {
|
|
413
|
+
parameterObject[key] = param.value === "true";
|
|
414
|
+
} else {
|
|
415
|
+
parameterObject[key] = param.value;
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const resourceObject: Record<string, any> = {};
|
|
421
|
+
if (data.resources && data.resources.length > 0) {
|
|
422
|
+
data.resources.forEach((resource) => {
|
|
423
|
+
if (resource.type === "volume") {
|
|
424
|
+
resourceObject[resource.name] = {
|
|
425
|
+
volume: {
|
|
426
|
+
kind: "storage",
|
|
427
|
+
size: parseInt(resource.value) || 1,
|
|
428
|
+
unit: "G",
|
|
429
|
+
type: resource.kind,
|
|
430
|
+
},
|
|
431
|
+
};
|
|
432
|
+
} else if (resource.type === "secret") {
|
|
433
|
+
resourceObject[resource.name] = {
|
|
434
|
+
secret: `${data.tenant}/${resource.value}`,
|
|
435
|
+
};
|
|
436
|
+
} else if (resource.type === "domain") {
|
|
437
|
+
resourceObject[resource.name] = {
|
|
438
|
+
domain: `${data.tenant}/${resource.value}`,
|
|
439
|
+
};
|
|
440
|
+
} else if (resource.type === "ca") {
|
|
441
|
+
resourceObject[resource.name] = {
|
|
442
|
+
ca: `${data.tenant}/${resource.value}`,
|
|
443
|
+
};
|
|
444
|
+
} else if (resource.type === "certificate") {
|
|
445
|
+
resourceObject[resource.name] = {
|
|
446
|
+
certificate: `${data.tenant}/${resource.value}`,
|
|
447
|
+
};
|
|
448
|
+
} else if (resource.type === "port") {
|
|
449
|
+
resourceObject[resource.name] = {
|
|
450
|
+
port: `${data.tenant}/${resource.value}`,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
let previousRevision = 1;
|
|
457
|
+
if (data.currentRevision) {
|
|
458
|
+
previousRevision = parseInt(data.currentRevision.toString(), 10);
|
|
459
|
+
if (isNaN(previousRevision)) {
|
|
460
|
+
console.warn("currentRevision is not a valid number, using 1");
|
|
461
|
+
previousRevision = 1;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
else{
|
|
465
|
+
previousRevision = getLatestRevision(data.revisions) || 1;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const scaleConfig: any = {};
|
|
469
|
+
if (data.role && data.role.length > 0) {
|
|
470
|
+
scaleConfig.detail = {};
|
|
471
|
+
data.role.forEach((role) => {
|
|
472
|
+
scaleConfig.detail[role.name] = {
|
|
473
|
+
hsize: role.hsize || scale || data.minReplicas || 1,
|
|
474
|
+
};
|
|
475
|
+
});
|
|
476
|
+
} else {
|
|
477
|
+
scaleConfig.hsize = scale || data.minReplicas || 1;
|
|
478
|
+
}
|
|
479
|
+
const meta = {
|
|
480
|
+
scaling: {
|
|
481
|
+
simple:
|
|
482
|
+
data.role?.reduce((acc, role) => {
|
|
483
|
+
if (role.scalling && role.name) {
|
|
484
|
+
acc[role.name] = {
|
|
485
|
+
scale_up: {
|
|
486
|
+
cpu: Math.min(parseInt(role.scalling.cpu.up) || 0, 100),
|
|
487
|
+
memory: Math.min(parseInt(role.scalling.memory.up) || 0, 100),
|
|
488
|
+
},
|
|
489
|
+
scale_down: {
|
|
490
|
+
cpu: Math.min(parseInt(role.scalling.cpu.down) || 0, 100),
|
|
491
|
+
memory: Math.min(
|
|
492
|
+
parseInt(role.scalling.memory.down) || 0,
|
|
493
|
+
100
|
|
494
|
+
),
|
|
495
|
+
},
|
|
496
|
+
hysteresis: parseInt(role.scalling.histeresys) || 0,
|
|
497
|
+
min_replicas: role.scalling.instances.min || 0,
|
|
498
|
+
max_replicas: role.scalling.instances.max || 0,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
return acc;
|
|
502
|
+
}, {} as Record<string, any>) || {},
|
|
503
|
+
},
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
const updateBody = {
|
|
507
|
+
spec: {
|
|
508
|
+
type: "update-config",
|
|
509
|
+
comment: "Service configuration update",
|
|
510
|
+
config: {
|
|
511
|
+
parameter: parameterObject,
|
|
512
|
+
resource: resourceObject,
|
|
513
|
+
resilience: 0,
|
|
514
|
+
scale: scaleConfig,
|
|
515
|
+
},
|
|
516
|
+
meta: meta,
|
|
517
|
+
},
|
|
518
|
+
tenant: data.tenant,
|
|
519
|
+
service: data.name,
|
|
520
|
+
previous: previousRevision,
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
const response = await makeGlobalWebSocketRequest(
|
|
524
|
+
"revision:update_revision",
|
|
525
|
+
updateBody,
|
|
526
|
+
30000,
|
|
527
|
+
"UPDATE_CONFIG",
|
|
528
|
+
data.name
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
const updatedService: Service = {
|
|
532
|
+
...data,
|
|
533
|
+
status: {
|
|
534
|
+
code: "PENDING",
|
|
535
|
+
message: "",
|
|
536
|
+
timestamp: "",
|
|
537
|
+
args: [],
|
|
538
|
+
},
|
|
539
|
+
};
|
|
540
|
+
eventHelper.service.publish.updated(updatedService);
|
|
541
|
+
|
|
542
|
+
const updateNotification: Notification = {
|
|
543
|
+
type: "success",
|
|
544
|
+
subtype: "service-updated",
|
|
545
|
+
date: Date.now().toString(),
|
|
546
|
+
status: "unread",
|
|
547
|
+
callToAction: false,
|
|
548
|
+
data: {
|
|
549
|
+
service: data.name,
|
|
550
|
+
tenant: data.tenant,
|
|
551
|
+
},
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
eventHelper.notification.publish.creation(updateNotification);
|
|
555
|
+
return response;
|
|
556
|
+
} catch (err) {
|
|
557
|
+
console.error("Error updating service configuration via WebSocket:", err);
|
|
558
|
+
const notification: Notification = {
|
|
559
|
+
type: "error",
|
|
560
|
+
subtype: "service-update-error",
|
|
561
|
+
date: Date.now().toString(),
|
|
562
|
+
info_content: {
|
|
563
|
+
code: (err as any).error.code,
|
|
564
|
+
message: (err as any).error.content,
|
|
565
|
+
},
|
|
566
|
+
status: "unread",
|
|
567
|
+
callToAction: false,
|
|
568
|
+
data: {
|
|
569
|
+
service: data.name,
|
|
570
|
+
tenant: data.tenant,
|
|
571
|
+
},
|
|
572
|
+
};
|
|
573
|
+
eventHelper.notification.publish.creation(notification);
|
|
574
|
+
eventHelper.service.publish.updateError(data);
|
|
575
|
+
throw err;
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
export const unlinkServices = async (service: Service, token: string) => {
|
|
579
|
+
const deleteLinks: Link[] = service.links.filter(
|
|
580
|
+
(link) => link.delete === true
|
|
581
|
+
);
|
|
582
|
+
if (deleteLinks.length > 0) {
|
|
583
|
+
await Promise.all(
|
|
584
|
+
deleteLinks.map(async (link) => {
|
|
585
|
+
try {
|
|
586
|
+
await initializeGlobalWebSocketClient(token);
|
|
587
|
+
const status = getWebSocketStatus();
|
|
588
|
+
const unlinkBody = generateLinkBody(service, link);
|
|
589
|
+
|
|
590
|
+
const unlinkResponse = await makeGlobalWebSocketRequest(
|
|
591
|
+
"link:unlink_service",
|
|
592
|
+
unlinkBody,
|
|
593
|
+
30000,
|
|
594
|
+
"UNLINK",
|
|
595
|
+
service.name
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
const unlinkNotification: Notification = {
|
|
599
|
+
type: "success",
|
|
600
|
+
subtype: "service-unlinking",
|
|
601
|
+
date: Date.now().toString(),
|
|
602
|
+
status: "unread",
|
|
603
|
+
callToAction: false,
|
|
604
|
+
data: {
|
|
605
|
+
service: service.name,
|
|
606
|
+
tenant: service.tenant,
|
|
607
|
+
},
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
eventHelper.notification.publish.creation(unlinkNotification);
|
|
611
|
+
} catch (unlinkErr) {
|
|
612
|
+
console.error("Error unlinking service via WebSocket:", unlinkErr);
|
|
613
|
+
|
|
614
|
+
const notification: Notification = {
|
|
615
|
+
type: "error",
|
|
616
|
+
subtype: "service-unlink-error",
|
|
617
|
+
date: Date.now().toString(),
|
|
618
|
+
info_content: {
|
|
619
|
+
code: (unlinkErr as any).error.code,
|
|
620
|
+
message: (unlinkErr as any).error.content,
|
|
621
|
+
},
|
|
622
|
+
status: "unread",
|
|
623
|
+
callToAction: false,
|
|
624
|
+
data: {
|
|
625
|
+
service: service.name,
|
|
626
|
+
tenant: service.tenant,
|
|
627
|
+
},
|
|
628
|
+
};
|
|
629
|
+
eventHelper.notification.publish.creation(notification);
|
|
630
|
+
}
|
|
631
|
+
})
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
export const updateServiceLinks = async (link: Link, token: string) => {
|
|
636
|
+
const deleteLink = link.delete || false;
|
|
637
|
+
const originClient = link.client === link.originChannel;
|
|
638
|
+
const linkBody = {
|
|
639
|
+
client_tenant: link.tenant,
|
|
640
|
+
client_service: originClient ? link.origin : link.target,
|
|
641
|
+
client_channel: originClient ? link.originChannel : link.targetChannel,
|
|
642
|
+
server_tenant: link.tenant,
|
|
643
|
+
server_service: originClient ? link.target : link.origin,
|
|
644
|
+
server_channel: originClient ? link.targetChannel : link.originChannel,
|
|
645
|
+
};
|
|
646
|
+
if (deleteLink) {
|
|
647
|
+
try {
|
|
648
|
+
await initializeGlobalWebSocketClient(token);
|
|
649
|
+
const status = getWebSocketStatus();
|
|
650
|
+
|
|
651
|
+
const unlinkResponse = await makeGlobalWebSocketRequest(
|
|
652
|
+
"link:unlink_service",
|
|
653
|
+
linkBody,
|
|
654
|
+
30000,
|
|
655
|
+
"UNLINK",
|
|
656
|
+
link.origin
|
|
657
|
+
);
|
|
658
|
+
|
|
659
|
+
// const unlinkNotification: Notification = {
|
|
660
|
+
// type: "success",
|
|
661
|
+
// subtype: "service-unlinked",
|
|
662
|
+
// date: Date.now().toString(),
|
|
663
|
+
// status: "unread",
|
|
664
|
+
// callToAction: false,
|
|
665
|
+
// data: {
|
|
666
|
+
// service: link.origin,
|
|
667
|
+
// tenant: link.tenant,
|
|
668
|
+
// },
|
|
669
|
+
// };
|
|
670
|
+
|
|
671
|
+
// eventHelper.notification.publish.creation(unlinkNotification);
|
|
672
|
+
} catch (unlinkErr) {
|
|
673
|
+
console.error("Error unlinking service via WebSocket:", unlinkErr);
|
|
674
|
+
|
|
675
|
+
const notification: Notification = {
|
|
676
|
+
type: "error",
|
|
677
|
+
subtype: "service-unlink-error",
|
|
678
|
+
date: Date.now().toString(),
|
|
679
|
+
info_content: {
|
|
680
|
+
code: (unlinkErr as any).error.code,
|
|
681
|
+
message: (unlinkErr as any).error.content,
|
|
682
|
+
},
|
|
683
|
+
status: "unread",
|
|
684
|
+
callToAction: false,
|
|
685
|
+
data: {
|
|
686
|
+
service: link.origin,
|
|
687
|
+
tenant: link.tenant,
|
|
688
|
+
},
|
|
689
|
+
};
|
|
690
|
+
eventHelper.notification.publish.creation(notification);
|
|
691
|
+
}
|
|
692
|
+
} else {
|
|
693
|
+
try {
|
|
694
|
+
await initializeGlobalWebSocketClient(token);
|
|
695
|
+
const status = getWebSocketStatus();
|
|
696
|
+
|
|
697
|
+
const linkResponse = await makeGlobalWebSocketRequest(
|
|
698
|
+
"link:link_service",
|
|
699
|
+
linkBody,
|
|
700
|
+
30000,
|
|
701
|
+
"LINK",
|
|
702
|
+
link.origin
|
|
703
|
+
);
|
|
704
|
+
|
|
705
|
+
const notification: Notification = {
|
|
706
|
+
type: "success",
|
|
707
|
+
subtype: "service-linked",
|
|
708
|
+
date: Date.now().toString(),
|
|
709
|
+
status: "unread",
|
|
710
|
+
callToAction: false,
|
|
711
|
+
data: {
|
|
712
|
+
service: link.origin,
|
|
713
|
+
tenant: link.tenant,
|
|
714
|
+
},
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
eventHelper.notification.publish.creation(notification);
|
|
718
|
+
} catch (linkErr) {
|
|
719
|
+
const notification: Notification = {
|
|
720
|
+
type: "error",
|
|
721
|
+
subtype: "error-service-link",
|
|
722
|
+
date: Date.now().toString(),
|
|
723
|
+
info_content: {
|
|
724
|
+
code: (linkErr as any).error.code,
|
|
725
|
+
message: (linkErr as any).error.content,
|
|
726
|
+
},
|
|
727
|
+
status: "unread",
|
|
728
|
+
callToAction: false,
|
|
729
|
+
data: {
|
|
730
|
+
service: link.origin,
|
|
731
|
+
tenant: link.tenant,
|
|
732
|
+
},
|
|
733
|
+
};
|
|
734
|
+
eventHelper.notification.publish.creation(notification);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
export const changeRevision = async (data: Service, token: string) => {
|
|
739
|
+
try {
|
|
740
|
+
await initializeGlobalWebSocketClient(token);
|
|
741
|
+
const status = getWebSocketStatus();
|
|
742
|
+
const revisionBody = {
|
|
743
|
+
tenant: data.tenant,
|
|
744
|
+
service: data.name,
|
|
745
|
+
previous: Number(data.lastDeployed),
|
|
746
|
+
spec: {
|
|
747
|
+
type: "rollback",
|
|
748
|
+
labels: {},
|
|
749
|
+
target: Number(data.currentRevision),
|
|
750
|
+
comment: "",
|
|
751
|
+
},
|
|
752
|
+
};
|
|
753
|
+
const revisionResponse = await makeGlobalWebSocketRequest(
|
|
754
|
+
"revision:update_revision",
|
|
755
|
+
revisionBody,
|
|
756
|
+
30000,
|
|
757
|
+
"UPDATE_REVISION",
|
|
758
|
+
data.name
|
|
759
|
+
);
|
|
760
|
+
const notification: Notification = {
|
|
761
|
+
type: "success",
|
|
762
|
+
subtype: "revision-updated",
|
|
763
|
+
date: Date.now().toString(),
|
|
764
|
+
status: "unread",
|
|
765
|
+
callToAction: false,
|
|
766
|
+
data: {
|
|
767
|
+
service: data,
|
|
768
|
+
},
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
eventHelper.notification.publish.creation(notification);
|
|
772
|
+
} catch (error) {
|
|
773
|
+
const notification: Notification = {
|
|
774
|
+
type: "error",
|
|
775
|
+
subtype: "error-revision-update",
|
|
776
|
+
date: Date.now().toString(),
|
|
777
|
+
info_content: {
|
|
778
|
+
code: (error as any).error.code,
|
|
779
|
+
message: (error as any).error.content,
|
|
780
|
+
},
|
|
781
|
+
status: "unread",
|
|
782
|
+
callToAction: false,
|
|
783
|
+
data: {
|
|
784
|
+
service: data,
|
|
785
|
+
},
|
|
786
|
+
};
|
|
787
|
+
eventHelper.notification.publish.creation(notification);
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
function getLatestRevision(revisions: string[]): number | null {
|
|
791
|
+
if (!revisions || revisions.length === 0) {
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
return Math.max(...revisions.map(Number));
|
|
796
|
+
}
|