@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,289 @@
|
|
|
1
|
+
import { Account, Environment, Notification } from "@hestekumori/aurora-interfaces";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
interface HandleEnvironmentEventParams {
|
|
6
|
+
entityId: string;
|
|
7
|
+
eventData: any;
|
|
8
|
+
parentParts: { [entity: string]: string };
|
|
9
|
+
accountsMap: Map<string, Account>;
|
|
10
|
+
environmentsMap: Map<string, Environment>;
|
|
11
|
+
pendingCloudProviderUpdates: Array<{ environmentId: string; accountId: string }>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface HandleEnvironmentEventResult {
|
|
15
|
+
environment: Environment;
|
|
16
|
+
pendingCloudProviderUpdate: { environmentId: string; accountId: string } | null;
|
|
17
|
+
hasTenant: boolean;
|
|
18
|
+
tenantId: string;
|
|
19
|
+
}
|
|
20
|
+
interface HandleEnvironmentOperationSuccessParams {
|
|
21
|
+
action: string;
|
|
22
|
+
entityName: string;
|
|
23
|
+
originalData: Environment;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface HandleEnvironmentOperationSuccessResult {
|
|
27
|
+
updatedEnvironment: Environment | null;
|
|
28
|
+
shouldDelete: boolean;
|
|
29
|
+
notification: Notification | null;
|
|
30
|
+
eventType: "created" | "updated" | "cleaned" | null;
|
|
31
|
+
}
|
|
32
|
+
interface HandleEnvironmentOperationErrorParams {
|
|
33
|
+
action: string;
|
|
34
|
+
entityName: string;
|
|
35
|
+
originalData: Environment;
|
|
36
|
+
error: any;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface HandleEnvironmentOperationErrorResult {
|
|
40
|
+
updatedEnvironment: Environment;
|
|
41
|
+
notification: Notification;
|
|
42
|
+
eventType: "creationError" | "updateError" | "deletionError";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Handles the "environment" event from WebSocket messages
|
|
47
|
+
* Processes environment data updates and cloud provider associations
|
|
48
|
+
*/
|
|
49
|
+
export const handleEnvironmentEvent = ({
|
|
50
|
+
entityId,
|
|
51
|
+
eventData,
|
|
52
|
+
parentParts,
|
|
53
|
+
accountsMap,
|
|
54
|
+
environmentsMap,
|
|
55
|
+
pendingCloudProviderUpdates,
|
|
56
|
+
}: HandleEnvironmentEventParams): HandleEnvironmentEventResult => {
|
|
57
|
+
const tenantId = parentParts.tenant;
|
|
58
|
+
const accountId = parentParts.account;
|
|
59
|
+
const relatedAccount = accountsMap.get(accountId);
|
|
60
|
+
let cloudProviderName = relatedAccount?.cloudProvider?.name || "";
|
|
61
|
+
|
|
62
|
+
let pendingCloudProviderUpdate: { environmentId: string; accountId: string } | null = null;
|
|
63
|
+
|
|
64
|
+
if (!relatedAccount) {
|
|
65
|
+
pendingCloudProviderUpdate = {
|
|
66
|
+
environmentId: entityId,
|
|
67
|
+
accountId: accountId,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const existingEnvironment = environmentsMap.get(entityId);
|
|
72
|
+
|
|
73
|
+
const newEnvironment: Environment = {
|
|
74
|
+
id: entityId,
|
|
75
|
+
name: entityId,
|
|
76
|
+
account: accountId,
|
|
77
|
+
tenant: tenantId,
|
|
78
|
+
logo: "",
|
|
79
|
+
services: [],
|
|
80
|
+
domains: [],
|
|
81
|
+
status: eventData.meta?.deleted
|
|
82
|
+
? {
|
|
83
|
+
code: "DELETING",
|
|
84
|
+
message: `Environment ${entityId} is being deleted.`,
|
|
85
|
+
timestamp: new Date().toISOString(),
|
|
86
|
+
}
|
|
87
|
+
: {
|
|
88
|
+
code: eventData.status.state.code,
|
|
89
|
+
message: eventData.status.state.message,
|
|
90
|
+
timestamp: eventData.status.state.timestamp,
|
|
91
|
+
},
|
|
92
|
+
highAvaliable: eventData.spec.highlyAvailable || false,
|
|
93
|
+
usage: {
|
|
94
|
+
current: {
|
|
95
|
+
cpu: eventData.status.marks.vcpu.current / 1000,
|
|
96
|
+
memory: eventData.status.marks.memory.current / 1000,
|
|
97
|
+
storage: eventData.status.marks.storage.current / 1000,
|
|
98
|
+
volatileStorage: eventData.status.marks.vstorage.current / 1000,
|
|
99
|
+
nonReplicatedStorage: eventData.status.marks.nrstorage.current / 1000,
|
|
100
|
+
persistentStorage: eventData.status.marks.rstorage.current / 1000,
|
|
101
|
+
nodes: eventData.status.nodes.length || 0,
|
|
102
|
+
memoryConsuption: existingEnvironment?.usage.current.memoryConsuption || [],
|
|
103
|
+
cpuConsuption: existingEnvironment?.usage.current.cpuConsuption || [],
|
|
104
|
+
},
|
|
105
|
+
limit: {
|
|
106
|
+
cpu: {
|
|
107
|
+
max: eventData.spec.marks.vcpu.highmark / 1000,
|
|
108
|
+
min: eventData.spec.marks.vcpu.lowmark / 1000,
|
|
109
|
+
},
|
|
110
|
+
memory: {
|
|
111
|
+
max: eventData.spec.marks.memory.highmark / 1000,
|
|
112
|
+
min: eventData.spec.marks.memory.lowmark / 1000,
|
|
113
|
+
},
|
|
114
|
+
storage: {
|
|
115
|
+
max: eventData.spec.marks.storage.highmark / 1000,
|
|
116
|
+
min: eventData.spec.marks.storage.lowmark / 1000,
|
|
117
|
+
},
|
|
118
|
+
volatileStorage: {
|
|
119
|
+
max: eventData.spec.marks.vstorage.highmark / 1000,
|
|
120
|
+
min: eventData.spec.marks.vstorage.lowmark / 1000,
|
|
121
|
+
},
|
|
122
|
+
nonReplicatedStorage: {
|
|
123
|
+
max: eventData.spec.marks.nrstorage.highmark / 1000,
|
|
124
|
+
min: eventData.spec.marks.nrstorage.lowmark / 1000,
|
|
125
|
+
},
|
|
126
|
+
persistentStorage: {
|
|
127
|
+
max: eventData.spec.marks.rstorage.highmark / 1000,
|
|
128
|
+
min: eventData.spec.marks.rstorage.lowmark / 1000,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
cost: eventData.status.marks.cost.current,
|
|
132
|
+
},
|
|
133
|
+
organization: "",
|
|
134
|
+
cloudProvider: cloudProviderName,
|
|
135
|
+
labels: Object.values(eventData.meta.labels),
|
|
136
|
+
nodes: {
|
|
137
|
+
max: eventData.spec.marks.nodes.highmark,
|
|
138
|
+
min: eventData.status.minimumRequiredNodes?.ingress?.replicas || 0,
|
|
139
|
+
},
|
|
140
|
+
type: eventData.spec.type || "primary",
|
|
141
|
+
cluster: { name: eventData.spec.cluster?.name || "" },
|
|
142
|
+
isIsolated: eventData.spec.isolated || false,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
environment: newEnvironment,
|
|
147
|
+
pendingCloudProviderUpdate,
|
|
148
|
+
hasTenant: false,
|
|
149
|
+
tenantId,
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Handles successful environment operations (CREATE, UPDATE, DELETE, CLEAN)
|
|
155
|
+
*/
|
|
156
|
+
export const handleEnvironmentOperationSuccess = ({
|
|
157
|
+
action,
|
|
158
|
+
entityName,
|
|
159
|
+
originalData,
|
|
160
|
+
}: HandleEnvironmentOperationSuccessParams): HandleEnvironmentOperationSuccessResult => {
|
|
161
|
+
const envData = originalData;
|
|
162
|
+
|
|
163
|
+
if (action === "DELETE") {
|
|
164
|
+
return {
|
|
165
|
+
updatedEnvironment: null,
|
|
166
|
+
shouldDelete: true,
|
|
167
|
+
notification: null,
|
|
168
|
+
eventType: null,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (action === "CLEAN") {
|
|
173
|
+
const updatedEnvironment: Environment = {
|
|
174
|
+
...envData,
|
|
175
|
+
status: {
|
|
176
|
+
code: "CLEANING_ENVIRONMENT",
|
|
177
|
+
message: `Environment ${envData.name} is being cleaned.`,
|
|
178
|
+
timestamp: new Date().toISOString(),
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const envNotification: Notification = {
|
|
183
|
+
type: "success",
|
|
184
|
+
subtype: "environment-cleaned",
|
|
185
|
+
date: Date.now().toString(),
|
|
186
|
+
status: "unread",
|
|
187
|
+
callToAction: false,
|
|
188
|
+
data: {
|
|
189
|
+
environment: envData.name,
|
|
190
|
+
account: envData.account,
|
|
191
|
+
tenant: envData.tenant,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
updatedEnvironment,
|
|
197
|
+
shouldDelete: false,
|
|
198
|
+
notification: envNotification,
|
|
199
|
+
eventType: "cleaned",
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (envData) {
|
|
203
|
+
const updatedEnvironment: Environment = {
|
|
204
|
+
...envData,
|
|
205
|
+
status: {
|
|
206
|
+
code: "ENVIRONMENT_READY",
|
|
207
|
+
message: `Environment ${envData.name} is ready.`,
|
|
208
|
+
timestamp: new Date().toISOString(),
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
let eventType: "created" | "updated" | null = null;
|
|
213
|
+
if (action === "CREATE") {
|
|
214
|
+
eventType = "created";
|
|
215
|
+
} else if (action === "UPDATE") {
|
|
216
|
+
eventType = "updated";
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
updatedEnvironment,
|
|
221
|
+
shouldDelete: false,
|
|
222
|
+
notification: null,
|
|
223
|
+
eventType,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
updatedEnvironment: null,
|
|
229
|
+
shouldDelete: false,
|
|
230
|
+
notification: null,
|
|
231
|
+
eventType: null,
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* Handles failed environment operations (CREATE, UPDATE, DELETE)
|
|
236
|
+
*/
|
|
237
|
+
export const handleEnvironmentOperationError = ({
|
|
238
|
+
action,
|
|
239
|
+
entityName,
|
|
240
|
+
originalData,
|
|
241
|
+
error,
|
|
242
|
+
}: HandleEnvironmentOperationErrorParams): HandleEnvironmentOperationErrorResult => {
|
|
243
|
+
let subtype: string;
|
|
244
|
+
let eventType: "creationError" | "updateError" | "deletionError";
|
|
245
|
+
|
|
246
|
+
if (action === "CREATE") {
|
|
247
|
+
subtype = "environment-creation-error";
|
|
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
|
+
}
|
|
256
|
+
|
|
257
|
+
const envErrorNotification: Notification = {
|
|
258
|
+
type: "error",
|
|
259
|
+
subtype,
|
|
260
|
+
date: Date.now().toString(),
|
|
261
|
+
status: "unread",
|
|
262
|
+
info_content: {
|
|
263
|
+
code: error?.error?.code || "UNKNOWN_ERROR",
|
|
264
|
+
message: error?.error?.content || error?.error?.message || "Unknown error",
|
|
265
|
+
timestamp: error?.error?.timestamp || "",
|
|
266
|
+
},
|
|
267
|
+
callToAction: false,
|
|
268
|
+
data: {
|
|
269
|
+
environment: originalData.name,
|
|
270
|
+
account: originalData.account,
|
|
271
|
+
tenant: originalData.tenant,
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const updatedEnvironment: Environment = {
|
|
276
|
+
...originalData,
|
|
277
|
+
status: {
|
|
278
|
+
code: "ERROR",
|
|
279
|
+
message: error?.error?.content || error?.error?.message || "Operation failed",
|
|
280
|
+
timestamp: new Date().toISOString(),
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
updatedEnvironment,
|
|
286
|
+
notification: envErrorNotification,
|
|
287
|
+
eventType,
|
|
288
|
+
};
|
|
289
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Link, Service } from "@hestekumori/aurora-interfaces";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
interface HandleLinkEventParams {
|
|
5
|
+
eventData: any;
|
|
6
|
+
servicesMap: Map<string, Service>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface HandleLinkEventResult {
|
|
10
|
+
linkId: string;
|
|
11
|
+
clientToServerLink: Link;
|
|
12
|
+
serverToClientLink: Link;
|
|
13
|
+
linkServiceServer: string;
|
|
14
|
+
linkServiceClient: string;
|
|
15
|
+
updatedServerService: Service | null;
|
|
16
|
+
updatedClientService: Service | null;
|
|
17
|
+
serverServiceFound: boolean;
|
|
18
|
+
clientServiceFound: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Handles the "link" event from WebSocket messages
|
|
22
|
+
* Creates bidirectional links between services
|
|
23
|
+
*/
|
|
24
|
+
export const handleLinkEvent = ({
|
|
25
|
+
eventData,
|
|
26
|
+
servicesMap,
|
|
27
|
+
}: HandleLinkEventParams): HandleLinkEventResult => {
|
|
28
|
+
const linkId = eventData.id.name;
|
|
29
|
+
const clientService = eventData.spec.client_service;
|
|
30
|
+
const clientChannel = eventData.spec.client_channel;
|
|
31
|
+
const serverService = eventData.spec.server_service;
|
|
32
|
+
const serverChannel = eventData.spec.server_channel;
|
|
33
|
+
const linkServiceServer = `${eventData.spec.server_tenant}/${serverService}`;
|
|
34
|
+
const linkServiceClient = `${eventData.spec.client_tenant}/${clientService}`;
|
|
35
|
+
const clientToServerLink: Link = {
|
|
36
|
+
name: linkId,
|
|
37
|
+
origin: clientService,
|
|
38
|
+
target: serverService,
|
|
39
|
+
originChannel: clientChannel,
|
|
40
|
+
targetChannel: serverChannel,
|
|
41
|
+
client: clientChannel,
|
|
42
|
+
server: serverChannel,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const serverToClientLink: Link = {
|
|
46
|
+
name: linkId,
|
|
47
|
+
origin: clientService,
|
|
48
|
+
target: serverService,
|
|
49
|
+
originChannel: clientChannel,
|
|
50
|
+
targetChannel: serverChannel,
|
|
51
|
+
client: clientChannel,
|
|
52
|
+
server: serverChannel,
|
|
53
|
+
};
|
|
54
|
+
let updatedServerService: Service | null = null;
|
|
55
|
+
let serverServiceFound = false;
|
|
56
|
+
|
|
57
|
+
const serverServiceObj = servicesMap.get(linkServiceServer);
|
|
58
|
+
if (serverServiceObj) {
|
|
59
|
+
serverServiceFound = true;
|
|
60
|
+
const existingLinkIndex = serverServiceObj.links.findIndex(
|
|
61
|
+
(link) =>
|
|
62
|
+
link.name === clientToServerLink.name &&
|
|
63
|
+
link.origin === clientToServerLink.origin &&
|
|
64
|
+
link.target === clientToServerLink.target
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
if (existingLinkIndex !== -1) {
|
|
68
|
+
serverServiceObj.links[existingLinkIndex] = clientToServerLink;
|
|
69
|
+
} else {
|
|
70
|
+
serverServiceObj.links.push(clientToServerLink);
|
|
71
|
+
}
|
|
72
|
+
updatedServerService = serverServiceObj;
|
|
73
|
+
} else {
|
|
74
|
+
console.warn(
|
|
75
|
+
`Client service ${linkServiceServer} not found when processing link event`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
let updatedClientService: Service | null = null;
|
|
79
|
+
let clientServiceFound = false;
|
|
80
|
+
|
|
81
|
+
const clientServiceObj = servicesMap.get(linkServiceClient);
|
|
82
|
+
if (clientServiceObj) {
|
|
83
|
+
clientServiceFound = true;
|
|
84
|
+
const existingLinkIndex = clientServiceObj.links.findIndex(
|
|
85
|
+
(link) =>
|
|
86
|
+
link.name === serverToClientLink.name &&
|
|
87
|
+
link.origin === serverToClientLink.origin &&
|
|
88
|
+
link.target === serverToClientLink.target
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (existingLinkIndex !== -1) {
|
|
92
|
+
clientServiceObj.links[existingLinkIndex] = serverToClientLink;
|
|
93
|
+
} else {
|
|
94
|
+
clientServiceObj.links.push(serverToClientLink);
|
|
95
|
+
}
|
|
96
|
+
updatedClientService = clientServiceObj;
|
|
97
|
+
} else {
|
|
98
|
+
console.warn(
|
|
99
|
+
`Server service ${linkServiceClient} not found when processing link event`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
linkId,
|
|
105
|
+
clientToServerLink,
|
|
106
|
+
serverToClientLink,
|
|
107
|
+
linkServiceServer,
|
|
108
|
+
linkServiceClient,
|
|
109
|
+
updatedServerService,
|
|
110
|
+
updatedClientService,
|
|
111
|
+
serverServiceFound,
|
|
112
|
+
clientServiceFound,
|
|
113
|
+
};
|
|
114
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Plan, UserData } from "@hestekumori/aurora-interfaces";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
interface HandlePlanInstanceEventParams {
|
|
5
|
+
entityId: string;
|
|
6
|
+
eventData: any;
|
|
7
|
+
parentParts: { [entity: string]: string };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface HandlePlanInstanceEventResult {
|
|
11
|
+
plan: Plan;
|
|
12
|
+
planKey: string;
|
|
13
|
+
}
|
|
14
|
+
interface UpdateUserPlansParams {
|
|
15
|
+
newPlan: Plan;
|
|
16
|
+
userData: UserData;
|
|
17
|
+
userId: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface UpdateUserPlansResult {
|
|
21
|
+
shouldUpdate: boolean;
|
|
22
|
+
updatedPlans: Plan[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Handles the "planinstance" event from WebSocket messages
|
|
27
|
+
* Processes plan data and builds the Plan object
|
|
28
|
+
*/
|
|
29
|
+
export const handlePlanInstanceEvent = ({
|
|
30
|
+
entityId,
|
|
31
|
+
eventData,
|
|
32
|
+
parentParts,
|
|
33
|
+
}: HandlePlanInstanceEventParams): HandlePlanInstanceEventResult => {
|
|
34
|
+
const planProvider = parentParts.planprovider;
|
|
35
|
+
const planKey = `${planProvider}/${entityId}`;
|
|
36
|
+
|
|
37
|
+
const newPlan: Plan = {
|
|
38
|
+
name: entityId,
|
|
39
|
+
provider: planProvider,
|
|
40
|
+
alias: eventData.spec.alias || "",
|
|
41
|
+
lastCheck: eventData.spec.lastCheck
|
|
42
|
+
? eventData.spec.lastCheck.toString()
|
|
43
|
+
: "",
|
|
44
|
+
tenants: eventData.status.tenants
|
|
45
|
+
? Object.keys(eventData.status.tenants)
|
|
46
|
+
: [],
|
|
47
|
+
quarantine: eventData.spec.quarantine || false,
|
|
48
|
+
owner: eventData.spec.owner || "",
|
|
49
|
+
limits: eventData.spec.limits
|
|
50
|
+
? {
|
|
51
|
+
cpu: eventData.spec.limits.vcpu || 0,
|
|
52
|
+
memory: eventData.spec.limits.memory || 0,
|
|
53
|
+
instances: eventData.spec.limits.instances || 0,
|
|
54
|
+
deployments: eventData.spec.limits.deployments || 0,
|
|
55
|
+
tenants: eventData.spec.limits.tenants || 0,
|
|
56
|
+
accounts: eventData.spec.limits.accounts || 0,
|
|
57
|
+
clusters: eventData.spec.limits.clusters || 0,
|
|
58
|
+
nrstorage: eventData.spec.limits.nrstorage || 0,
|
|
59
|
+
rstorage: eventData.spec.limits.rstorage || 0,
|
|
60
|
+
vstorage: eventData.spec.limits.vstorage || 0,
|
|
61
|
+
storage: eventData.spec.limits.storage || 0,
|
|
62
|
+
}
|
|
63
|
+
: undefined,
|
|
64
|
+
type: eventData.spec.type || "",
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
plan: newPlan,
|
|
69
|
+
planKey,
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Updates user plans after a plan event
|
|
75
|
+
* Only updates if the user is the owner of the plan
|
|
76
|
+
*/
|
|
77
|
+
export const updateUserPlansAfterPlanEvent = ({
|
|
78
|
+
newPlan,
|
|
79
|
+
userData,
|
|
80
|
+
userId,
|
|
81
|
+
}: UpdateUserPlansParams): UpdateUserPlansResult => {
|
|
82
|
+
if (newPlan.owner !== userId) {
|
|
83
|
+
return {
|
|
84
|
+
shouldUpdate: false,
|
|
85
|
+
updatedPlans: userData.plans || [],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const updatedPlans = userData.plans || [];
|
|
90
|
+
const existingIndex = updatedPlans.findIndex(
|
|
91
|
+
(plan) => plan.name === newPlan.name && plan.provider === newPlan.provider
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (existingIndex !== -1) {
|
|
95
|
+
updatedPlans[existingIndex] = newPlan;
|
|
96
|
+
} else {
|
|
97
|
+
updatedPlans.push(newPlan);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
shouldUpdate: true,
|
|
102
|
+
updatedPlans,
|
|
103
|
+
};
|
|
104
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
|
|
2
|
+
import { Registry, Tenant } from "@hestekumori/aurora-interfaces";
|
|
3
|
+
import { decodeRegistryAuth } from "../utils/utils";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
interface HandleRegistryEventParams {
|
|
7
|
+
eventData: any;
|
|
8
|
+
parentParts: { [entity: string]: string };
|
|
9
|
+
tenantsMap: Map<string, Tenant>;
|
|
10
|
+
secretsMap: Map<string, any>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface HandleRegistryEventResult {
|
|
14
|
+
registry: Registry;
|
|
15
|
+
tenantId: string;
|
|
16
|
+
tenantFound: boolean;
|
|
17
|
+
updatedTenant: Tenant | null;
|
|
18
|
+
pendingRegistry: { tenant: string; registry: Registry } | null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Extract registry credentials from secrets map
|
|
22
|
+
*/
|
|
23
|
+
const extractRegistryCredentials = (
|
|
24
|
+
tenantId: string,
|
|
25
|
+
secretName: string,
|
|
26
|
+
secretsMap: Map<string, any>
|
|
27
|
+
): { username: string; password: string } => {
|
|
28
|
+
let username = "";
|
|
29
|
+
let password = "";
|
|
30
|
+
|
|
31
|
+
const secretKey = `${tenantId}/${secretName}`;
|
|
32
|
+
const credentialsKey = `${secretKey}_credentials`;
|
|
33
|
+
const savedCredentials = secretsMap.get(credentialsKey);
|
|
34
|
+
if (savedCredentials) {
|
|
35
|
+
username = savedCredentials.secretUsername;
|
|
36
|
+
password = savedCredentials.secretPassword;
|
|
37
|
+
} else {
|
|
38
|
+
const secretData = secretsMap.get(secretKey);
|
|
39
|
+
if (secretData) {
|
|
40
|
+
try {
|
|
41
|
+
const parsedSecret = JSON.parse(secretData);
|
|
42
|
+
if (parsedSecret.auths) {
|
|
43
|
+
const registryUrl = Object.keys(parsedSecret.auths)[0];
|
|
44
|
+
if (registryUrl && parsedSecret.auths[registryUrl].auth) {
|
|
45
|
+
const credentials = decodeRegistryAuth(
|
|
46
|
+
parsedSecret.auths[registryUrl].auth
|
|
47
|
+
);
|
|
48
|
+
username = credentials.username;
|
|
49
|
+
password = credentials.password;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error("Error parsing registry secret in dregistry:", error);
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
console.warn("Secret not found for registry:", {
|
|
57
|
+
registryName: secretName,
|
|
58
|
+
secretKey,
|
|
59
|
+
availableSecrets: Array.from(secretsMap.keys()),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { username, password };
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Handles the "dregistry" event from WebSocket messages
|
|
69
|
+
* Processes registry data and credentials
|
|
70
|
+
*/
|
|
71
|
+
export const handleRegistryEvent = ({
|
|
72
|
+
eventData,
|
|
73
|
+
parentParts,
|
|
74
|
+
tenantsMap,
|
|
75
|
+
secretsMap,
|
|
76
|
+
}: HandleRegistryEventParams): HandleRegistryEventResult => {
|
|
77
|
+
const tenantId = parentParts.tenant;
|
|
78
|
+
const registryName = eventData.id?.name;
|
|
79
|
+
let username = "";
|
|
80
|
+
let password = "";
|
|
81
|
+
|
|
82
|
+
if (eventData.status?.secret?.name) {
|
|
83
|
+
const credentials = extractRegistryCredentials(
|
|
84
|
+
tenantId,
|
|
85
|
+
eventData.status.secret.name,
|
|
86
|
+
secretsMap
|
|
87
|
+
);
|
|
88
|
+
username = credentials.username;
|
|
89
|
+
password = credentials.password;
|
|
90
|
+
}
|
|
91
|
+
const newRegistry: Registry = {
|
|
92
|
+
name: registryName,
|
|
93
|
+
domain: eventData.spec.registry || "",
|
|
94
|
+
public: true,
|
|
95
|
+
credentials: username,
|
|
96
|
+
password: password,
|
|
97
|
+
description: `Registry for ${registryName}`,
|
|
98
|
+
extra: eventData.meta.labels.logo || "",
|
|
99
|
+
};
|
|
100
|
+
const tenant = tenantsMap.get(tenantId);
|
|
101
|
+
let tenantFound = false;
|
|
102
|
+
let updatedTenant: Tenant | null = null;
|
|
103
|
+
let pendingRegistry: { tenant: string; registry: Registry } | null = null;
|
|
104
|
+
|
|
105
|
+
if (tenant) {
|
|
106
|
+
tenantFound = true;
|
|
107
|
+
const existingIndex = tenant.registry.findIndex(
|
|
108
|
+
(reg) => reg.domain === registryName
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
if (existingIndex !== -1) {
|
|
112
|
+
tenant.registry[existingIndex] = newRegistry;
|
|
113
|
+
} else {
|
|
114
|
+
tenant.registry.push(newRegistry);
|
|
115
|
+
}
|
|
116
|
+
updatedTenant = tenant;
|
|
117
|
+
} else {
|
|
118
|
+
pendingRegistry = {
|
|
119
|
+
tenant: tenantId,
|
|
120
|
+
registry: newRegistry,
|
|
121
|
+
};
|
|
122
|
+
console.warn(
|
|
123
|
+
`Registry orphaned for tenant ${tenantId}, added to pending registries.`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
registry: newRegistry,
|
|
129
|
+
tenantId,
|
|
130
|
+
tenantFound,
|
|
131
|
+
updatedTenant,
|
|
132
|
+
pendingRegistry,
|
|
133
|
+
};
|
|
134
|
+
};
|