@abtnode/core 1.17.8-beta-20260109-075740-5f484e08 → 1.17.8-beta-20260111-112953-aed5ff39
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/lib/api/team/access-key-manager.js +104 -0
- package/lib/api/team/invitation-manager.js +461 -0
- package/lib/api/team/notification-manager.js +189 -0
- package/lib/api/team/oauth-manager.js +60 -0
- package/lib/api/team/org-crud-manager.js +202 -0
- package/lib/api/team/org-manager.js +56 -0
- package/lib/api/team/org-member-manager.js +403 -0
- package/lib/api/team/org-query-manager.js +126 -0
- package/lib/api/team/org-resource-manager.js +186 -0
- package/lib/api/team/passport-manager.js +670 -0
- package/lib/api/team/rbac-manager.js +335 -0
- package/lib/api/team/session-manager.js +540 -0
- package/lib/api/team/store-manager.js +198 -0
- package/lib/api/team/tag-manager.js +230 -0
- package/lib/api/team/user-auth-manager.js +132 -0
- package/lib/api/team/user-manager.js +78 -0
- package/lib/api/team/user-query-manager.js +299 -0
- package/lib/api/team/user-social-manager.js +354 -0
- package/lib/api/team/user-update-manager.js +224 -0
- package/lib/api/team/verify-code-manager.js +161 -0
- package/lib/api/team.js +439 -3287
- package/lib/blocklet/manager/disk/auth-manager.js +68 -0
- package/lib/blocklet/manager/disk/backup-manager.js +288 -0
- package/lib/blocklet/manager/disk/cleanup-manager.js +157 -0
- package/lib/blocklet/manager/disk/component-manager.js +83 -0
- package/lib/blocklet/manager/disk/config-manager.js +191 -0
- package/lib/blocklet/manager/disk/controller-manager.js +64 -0
- package/lib/blocklet/manager/disk/delete-reset-manager.js +328 -0
- package/lib/blocklet/manager/disk/download-manager.js +96 -0
- package/lib/blocklet/manager/disk/env-config-manager.js +311 -0
- package/lib/blocklet/manager/disk/federated-manager.js +651 -0
- package/lib/blocklet/manager/disk/hook-manager.js +124 -0
- package/lib/blocklet/manager/disk/install-component-manager.js +95 -0
- package/lib/blocklet/manager/disk/install-core-manager.js +448 -0
- package/lib/blocklet/manager/disk/install-download-manager.js +313 -0
- package/lib/blocklet/manager/disk/install-manager.js +36 -0
- package/lib/blocklet/manager/disk/install-upgrade-manager.js +340 -0
- package/lib/blocklet/manager/disk/job-manager.js +467 -0
- package/lib/blocklet/manager/disk/lifecycle-manager.js +26 -0
- package/lib/blocklet/manager/disk/notification-manager.js +343 -0
- package/lib/blocklet/manager/disk/query-manager.js +562 -0
- package/lib/blocklet/manager/disk/settings-manager.js +507 -0
- package/lib/blocklet/manager/disk/start-manager.js +611 -0
- package/lib/blocklet/manager/disk/stop-restart-manager.js +292 -0
- package/lib/blocklet/manager/disk/update-manager.js +153 -0
- package/lib/blocklet/manager/disk.js +669 -5796
- package/lib/blocklet/manager/helper/blue-green-start-blocklet.js +5 -0
- package/lib/blocklet/manager/lock.js +18 -0
- package/lib/event/index.js +28 -24
- package/lib/util/blocklet/app-utils.js +192 -0
- package/lib/util/blocklet/blocklet-loader.js +258 -0
- package/lib/util/blocklet/config-manager.js +232 -0
- package/lib/util/blocklet/did-document.js +240 -0
- package/lib/util/blocklet/environment.js +555 -0
- package/lib/util/blocklet/health-check.js +449 -0
- package/lib/util/blocklet/install-utils.js +365 -0
- package/lib/util/blocklet/logo.js +57 -0
- package/lib/util/blocklet/meta-utils.js +269 -0
- package/lib/util/blocklet/port-manager.js +141 -0
- package/lib/util/blocklet/process-manager.js +504 -0
- package/lib/util/blocklet/runtime-info.js +105 -0
- package/lib/util/blocklet/validation.js +418 -0
- package/lib/util/blocklet.js +98 -3066
- package/lib/util/wallet-app-notification.js +40 -0
- package/package.json +22 -22
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/* eslint-disable no-await-in-loop */
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:install-download');
|
|
4
|
+
const promiseSpawn = require('@abtnode/util/lib/promise-spawn');
|
|
5
|
+
const { INSTALL_ACTIONS } = require('@abtnode/constant');
|
|
6
|
+
const { BlockletStatus, BlockletEvents, fromBlockletStatus } = require('@blocklet/constant');
|
|
7
|
+
const { forEachComponentV2 } = require('@blocklet/meta/lib/util');
|
|
8
|
+
|
|
9
|
+
const states = require('../../../states');
|
|
10
|
+
const { getDisplayName } = require('../../../util/blocklet');
|
|
11
|
+
const { dockerExecChown } = require('../../../util/docker/docker-exec-chown');
|
|
12
|
+
const checkDockerRunHistory = require('../../../util/docker/check-docker-run-history');
|
|
13
|
+
const { blueGreenGetComponentIds } = require('../helper/blue-green-get-componentids.js');
|
|
14
|
+
const { blueGreenUpgradeBlocklet } = require('../helper/blue-green-upgrade-blocklet.js');
|
|
15
|
+
const { statusLock } = require('../lock');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Download and install blocklet
|
|
19
|
+
*/
|
|
20
|
+
async function _downloadAndInstall(manager, params, { _onInstall, _upgradeBlocklet }) {
|
|
21
|
+
const {
|
|
22
|
+
blocklet,
|
|
23
|
+
context,
|
|
24
|
+
postAction,
|
|
25
|
+
oldBlocklet,
|
|
26
|
+
throwOnError,
|
|
27
|
+
skipCheckStatusBeforeDownload,
|
|
28
|
+
componentDids,
|
|
29
|
+
skipCheckIntegrity,
|
|
30
|
+
shouldCleanUploadFile,
|
|
31
|
+
url,
|
|
32
|
+
} = params;
|
|
33
|
+
|
|
34
|
+
logger.info('_downloadAndInstall', { did: blocklet?.meta?.did });
|
|
35
|
+
const { meta } = blocklet;
|
|
36
|
+
const { name, did, version } = meta;
|
|
37
|
+
|
|
38
|
+
const nodeInfo = await states.node.read();
|
|
39
|
+
if (checkDockerRunHistory(nodeInfo)) {
|
|
40
|
+
const newBlocklet = await manager.getBlocklet(did);
|
|
41
|
+
// pull docker image
|
|
42
|
+
await forEachComponentV2(newBlocklet, async (component) => {
|
|
43
|
+
if (component?.meta?.docker?.image) {
|
|
44
|
+
await promiseSpawn(`docker pull ${component?.meta?.docker?.image}`, {}, { timeout: 120 * 1000, retry: 0 });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// check status
|
|
50
|
+
if (!skipCheckStatusBeforeDownload) {
|
|
51
|
+
await statusLock.acquire('skip-check-status-before-download');
|
|
52
|
+
try {
|
|
53
|
+
const b0 = await states.blocklet.getBlocklet(did);
|
|
54
|
+
if (!b0 || ![BlockletStatus.waiting].includes(b0.status)) {
|
|
55
|
+
if (!b0) {
|
|
56
|
+
throw new Error('blocklet does not exist before downloading');
|
|
57
|
+
} else {
|
|
58
|
+
logger.error(`blocklet status is invalid before downloading: ${fromBlockletStatus(b0.status)}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
logger.error('Check blocklet status failed before downloading', {
|
|
63
|
+
name,
|
|
64
|
+
did,
|
|
65
|
+
error,
|
|
66
|
+
});
|
|
67
|
+
await manager._rollback(postAction, did, oldBlocklet);
|
|
68
|
+
return;
|
|
69
|
+
} finally {
|
|
70
|
+
await statusLock.releaseLock('skip-check-status-before-download');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// download bundle
|
|
75
|
+
const childrenToDownload = (blocklet.children || []).filter((x) => {
|
|
76
|
+
if (componentDids?.length) {
|
|
77
|
+
return componentDids.includes(x.meta.did);
|
|
78
|
+
}
|
|
79
|
+
return x;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const blueGreenComponentIds = blueGreenGetComponentIds(oldBlocklet || blocklet, componentDids);
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
if (process.env.ABT_NODE_DISABLE_BLUE_GREEN === 'true' || process.env.ABT_NODE_DISABLE_BLUE_GREEN === '1') {
|
|
86
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
|
|
87
|
+
componentDids,
|
|
88
|
+
isGreen: false,
|
|
89
|
+
});
|
|
90
|
+
} else {
|
|
91
|
+
await Promise.all(
|
|
92
|
+
blueGreenComponentIds.map(async (item) => {
|
|
93
|
+
if (item.componentDids.length === 0) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
|
|
97
|
+
componentDids: item.componentDids,
|
|
98
|
+
isGreen: item.changeToGreen,
|
|
99
|
+
});
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const state = await states.blocklet.getBlocklet(did);
|
|
105
|
+
manager.emit(BlockletEvents.statusChange, state);
|
|
106
|
+
const { isCancelled } = await manager._downloadBlocklet(
|
|
107
|
+
{
|
|
108
|
+
...blocklet,
|
|
109
|
+
children: childrenToDownload,
|
|
110
|
+
},
|
|
111
|
+
Object.assign({}, context, { skipCheckIntegrity })
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
if (isCancelled) {
|
|
115
|
+
logger.info('Download was canceled', { name, did, version });
|
|
116
|
+
|
|
117
|
+
// rollback on download cancelled
|
|
118
|
+
await statusLock.acquire('download-cancelled');
|
|
119
|
+
try {
|
|
120
|
+
if ((await states.blocklet.getBlockletStatus(did)) === BlockletStatus.downloading) {
|
|
121
|
+
await manager._rollback(postAction, did, oldBlocklet);
|
|
122
|
+
}
|
|
123
|
+
} catch (error) {
|
|
124
|
+
logger.error('Rollback blocklet failed on download canceled', { postAction, name, did, version, error });
|
|
125
|
+
} finally {
|
|
126
|
+
await statusLock.releaseLock('download-cancelled');
|
|
127
|
+
}
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
} catch (err) {
|
|
131
|
+
if (err.message.includes('EACCES:')) {
|
|
132
|
+
if (checkDockerRunHistory(nodeInfo)) {
|
|
133
|
+
try {
|
|
134
|
+
await dockerExecChown({
|
|
135
|
+
name: `${did}-${blocklet.meta.name}-download`,
|
|
136
|
+
dirs: [
|
|
137
|
+
path.join(process.env.ABT_NODE_DATA_DIR, 'blocklets'),
|
|
138
|
+
path.join(process.env.ABT_NODE_DATA_DIR, 'tmp', 'pnpm-stores'),
|
|
139
|
+
],
|
|
140
|
+
});
|
|
141
|
+
} catch (error) {
|
|
142
|
+
logger.error('Retry download blocklet failed on chown', { did, name, version, error });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
logger.error('Download blocklet tarball failed', { name, did, version });
|
|
147
|
+
|
|
148
|
+
manager.emit(BlockletEvents.downloadFailed, {
|
|
149
|
+
meta: { did },
|
|
150
|
+
error: {
|
|
151
|
+
message: err.message,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
manager._createNotification(did, {
|
|
155
|
+
title: `${childrenToDownload.length > 1 ? `${childrenToDownload.length} components` : 'Component'} failed to download for ${getDisplayName(blocklet)}`,
|
|
156
|
+
description: `${childrenToDownload
|
|
157
|
+
.map((x) => `${x.meta.title}@${x.meta.version}`)
|
|
158
|
+
.join(', ')} download failed for ${getDisplayName(blocklet)}: ${err.message}`,
|
|
159
|
+
entityType: 'blocklet',
|
|
160
|
+
entityId: did,
|
|
161
|
+
severity: 'error',
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// rollback on download failed
|
|
165
|
+
await statusLock.acquire('rollback-on-download-failed');
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const status = await states.blocklet.getBlockletStatus(did);
|
|
169
|
+
if ([BlockletStatus.downloading, BlockletStatus.waiting].includes(status)) {
|
|
170
|
+
await manager._rollback(postAction, did, oldBlocklet);
|
|
171
|
+
}
|
|
172
|
+
} catch (error) {
|
|
173
|
+
logger.error('Rollback blocklet failed on download failed', { postAction, name, did, version, error });
|
|
174
|
+
} finally {
|
|
175
|
+
await statusLock.releaseLock('rollback-on-download-failed');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (throwOnError) {
|
|
179
|
+
throw err;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// update status
|
|
186
|
+
await statusLock.acquire('download-update-status');
|
|
187
|
+
try {
|
|
188
|
+
await Promise.all(
|
|
189
|
+
blueGreenComponentIds.map(async (item) => {
|
|
190
|
+
if (item.componentDids.length === 0) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if ((await states.blocklet.getBlockletStatus(did)) !== BlockletStatus.downloading) {
|
|
194
|
+
throw new Error('blocklet status changed durning download');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (postAction === INSTALL_ACTIONS.INSTALL) {
|
|
198
|
+
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.installing, {
|
|
199
|
+
componentDids: item.componentDids,
|
|
200
|
+
isGreen: item.changeToGreen,
|
|
201
|
+
});
|
|
202
|
+
manager.emit(BlockletEvents.statusChange, state);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (
|
|
206
|
+
[
|
|
207
|
+
INSTALL_ACTIONS.INSTALL_COMPONENT,
|
|
208
|
+
INSTALL_ACTIONS.UPGRADE_COMPONENT,
|
|
209
|
+
'upgrade', // for backward compatibility
|
|
210
|
+
].includes(postAction)
|
|
211
|
+
) {
|
|
212
|
+
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading, {
|
|
213
|
+
componentDids: item.componentDids,
|
|
214
|
+
isGreen: item.changeToGreen,
|
|
215
|
+
});
|
|
216
|
+
manager.emit(BlockletEvents.statusChange, state);
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
logger.error(error.message);
|
|
222
|
+
} finally {
|
|
223
|
+
await statusLock.releaseLock('download-update-status');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// install
|
|
227
|
+
try {
|
|
228
|
+
// install blocklet
|
|
229
|
+
if (postAction === INSTALL_ACTIONS.INSTALL) {
|
|
230
|
+
await _onInstall(manager, { blocklet, componentDids, context, oldBlocklet });
|
|
231
|
+
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// upgrade blocklet
|
|
236
|
+
if (
|
|
237
|
+
[
|
|
238
|
+
INSTALL_ACTIONS.INSTALL_COMPONENT,
|
|
239
|
+
INSTALL_ACTIONS.UPGRADE_COMPONENT,
|
|
240
|
+
'upgrade', // for backward compatibility
|
|
241
|
+
].includes(postAction)
|
|
242
|
+
) {
|
|
243
|
+
logger.info('do upgrade blocklet', { did, version, postAction, componentDids });
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
if (postAction === INSTALL_ACTIONS.UPGRADE_COMPONENT) {
|
|
247
|
+
if (process.env.ABT_NODE_DISABLE_BLUE_GREEN === 'true' || process.env.ABT_NODE_DISABLE_BLUE_GREEN === '1') {
|
|
248
|
+
await _upgradeBlocklet(manager, {
|
|
249
|
+
newBlocklet: blocklet,
|
|
250
|
+
oldBlocklet,
|
|
251
|
+
componentDids,
|
|
252
|
+
context,
|
|
253
|
+
action: postAction,
|
|
254
|
+
shouldCleanUploadFile,
|
|
255
|
+
url,
|
|
256
|
+
});
|
|
257
|
+
} else {
|
|
258
|
+
await blueGreenUpgradeBlocklet(
|
|
259
|
+
{
|
|
260
|
+
newBlocklet: blocklet,
|
|
261
|
+
oldBlocklet,
|
|
262
|
+
componentDids,
|
|
263
|
+
action: postAction,
|
|
264
|
+
shouldCleanUploadFile,
|
|
265
|
+
url,
|
|
266
|
+
},
|
|
267
|
+
context,
|
|
268
|
+
manager,
|
|
269
|
+
states
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
await _upgradeBlocklet(manager, {
|
|
274
|
+
newBlocklet: blocklet,
|
|
275
|
+
oldBlocklet,
|
|
276
|
+
componentDids,
|
|
277
|
+
action: postAction,
|
|
278
|
+
shouldCleanUploadFile,
|
|
279
|
+
url,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const newBlocklet = await manager.getBlocklet(did);
|
|
284
|
+
|
|
285
|
+
if (newBlocklet.controller) {
|
|
286
|
+
const eventType = 'install';
|
|
287
|
+
|
|
288
|
+
manager.reportComponentUsageQueue.push({
|
|
289
|
+
entity: 'blocklet',
|
|
290
|
+
action: 'reportComponentUsage',
|
|
291
|
+
time: new Date().toISOString(),
|
|
292
|
+
did,
|
|
293
|
+
componentDids,
|
|
294
|
+
eventType: 'install',
|
|
295
|
+
context,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
logger.info('pushed reporting install components event job to queue', { did, componentDids, eventType });
|
|
299
|
+
}
|
|
300
|
+
} catch (err) {
|
|
301
|
+
logger.error('blocklet onUpgrade error', { error: err });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
if (throwOnError) {
|
|
306
|
+
throw error;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
module.exports = {
|
|
312
|
+
_downloadAndInstall,
|
|
313
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install Manager - Orchestrates blocklet installation operations
|
|
3
|
+
*
|
|
4
|
+
* This module re-exports functionality from specialized sub-modules:
|
|
5
|
+
* - install-core-manager.js: Core installation functions (install, _installBlocklet, _addBlocklet, _onInstall)
|
|
6
|
+
* - install-download-manager.js: Download and install operations (_downloadAndInstall)
|
|
7
|
+
* - install-upgrade-manager.js: Upgrade operations (_upgradeBlocklet, _runMigration, _onRestart, etc.)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { _onInstall, _installBlocklet, _addBlocklet, install } = require('./install-core-manager');
|
|
11
|
+
const { _downloadAndInstall: _downloadAndInstallBase } = require('./install-download-manager');
|
|
12
|
+
const {
|
|
13
|
+
_onRestart,
|
|
14
|
+
_runMigration,
|
|
15
|
+
_cleanUploadFile,
|
|
16
|
+
_ensureDeletedChildrenInSettings,
|
|
17
|
+
_upgradeBlocklet,
|
|
18
|
+
} = require('./install-upgrade-manager');
|
|
19
|
+
|
|
20
|
+
// Wrap _downloadAndInstall to inject dependencies
|
|
21
|
+
function _downloadAndInstall(manager, params) {
|
|
22
|
+
return _downloadAndInstallBase(manager, params, { _onInstall, _upgradeBlocklet });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
_downloadAndInstall,
|
|
27
|
+
_onInstall,
|
|
28
|
+
_onRestart,
|
|
29
|
+
_installBlocklet,
|
|
30
|
+
_runMigration,
|
|
31
|
+
_cleanUploadFile,
|
|
32
|
+
_upgradeBlocklet,
|
|
33
|
+
_ensureDeletedChildrenInSettings,
|
|
34
|
+
_addBlocklet,
|
|
35
|
+
install,
|
|
36
|
+
};
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/* eslint-disable no-await-in-loop */
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:install-upgrade');
|
|
4
|
+
const { INSTALL_ACTIONS, WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
5
|
+
const { BlockletStatus, BlockletEvents, BlockletInternalEvents } = require('@blocklet/constant');
|
|
6
|
+
const {
|
|
7
|
+
forEachComponentV2,
|
|
8
|
+
forEachComponentV2Sync,
|
|
9
|
+
hasStartEngine,
|
|
10
|
+
isExternalBlocklet,
|
|
11
|
+
} = require('@blocklet/meta/lib/util');
|
|
12
|
+
const { getComponentsInternalInfo } = require('@blocklet/meta/lib/blocklet');
|
|
13
|
+
|
|
14
|
+
const states = require('../../../states');
|
|
15
|
+
const {
|
|
16
|
+
getRuntimeEnvironments,
|
|
17
|
+
getDisplayName,
|
|
18
|
+
getComponentNamesWithVersion,
|
|
19
|
+
getHookArgs,
|
|
20
|
+
updateBlockletFallbackLogo,
|
|
21
|
+
} = require('../../../util/blocklet');
|
|
22
|
+
const checkNeedRunDocker = require('../../../util/docker/check-need-run-docker');
|
|
23
|
+
const { dockerExec } = require('../../../util/docker/docker-exec');
|
|
24
|
+
const runBlockletMigrationScripts = require('../../migration');
|
|
25
|
+
const getMigrationScripts = require('../../../util/get-migration-scripts');
|
|
26
|
+
const { removeUploadFile } = require('../../downloader/bundle-downloader');
|
|
27
|
+
const { blueGreenStartBlocklet } = require('../helper/blue-green-start-blocklet.js');
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* On restart callback
|
|
31
|
+
*/
|
|
32
|
+
async function _onRestart(manager, { did, componentDids, context, operator }) {
|
|
33
|
+
// 检查 blocklet 是否存在,如果不存在则跳过重启任务
|
|
34
|
+
// 这可以防止在 blocklet 被删除后,队列中待处理的重启任务执行时出错
|
|
35
|
+
if (!(await manager.hasBlocklet({ did }))) {
|
|
36
|
+
logger.warn('skip restart job: blocklet not found', { did });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (process.env.ABT_NODE_DISABLE_BLUE_GREEN) {
|
|
41
|
+
await manager.stop({ did, componentDids, context, operator });
|
|
42
|
+
await manager.start({ did, componentDids, checkHealthImmediately: true });
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
await blueGreenStartBlocklet({ did, componentDids, operator }, context, manager, states);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Run migration scripts
|
|
50
|
+
*/
|
|
51
|
+
async function _runMigration(manager, { parallel, did, blocklet, oldBlocklet, componentDids }) {
|
|
52
|
+
logger.info('start migration on upgrading', { did, componentDids });
|
|
53
|
+
const oldVersions = {};
|
|
54
|
+
forEachComponentV2Sync(oldBlocklet, (b) => {
|
|
55
|
+
if (componentDids.includes(b.meta.did)) {
|
|
56
|
+
oldVersions[b.meta.did] = b.meta.version;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
const nodeInfo = await states.node.read();
|
|
60
|
+
// 等待磁盘映射完成
|
|
61
|
+
|
|
62
|
+
const nodeEnvironments = await states.node.getEnvironments(nodeInfo);
|
|
63
|
+
|
|
64
|
+
const runMigration = async (b) => {
|
|
65
|
+
if (!componentDids.includes(b.meta.did)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const nextEnv = getRuntimeEnvironments(b, nodeEnvironments, [blocklet]);
|
|
69
|
+
const subNeedRunDocker = await checkNeedRunDocker(b.meta, nextEnv, nodeInfo, isExternalBlocklet(blocklet));
|
|
70
|
+
const hookArgs = getHookArgs(b);
|
|
71
|
+
|
|
72
|
+
const scriptsDir = path.join(b.env.appDir, 'migration');
|
|
73
|
+
const scripts = await getMigrationScripts(scriptsDir, oldVersions[b.meta.did]);
|
|
74
|
+
if (!scripts.length) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
if (subNeedRunDocker) {
|
|
79
|
+
if (scripts.length) {
|
|
80
|
+
await dockerExec({
|
|
81
|
+
blocklet,
|
|
82
|
+
meta: b.meta,
|
|
83
|
+
env: nextEnv,
|
|
84
|
+
script: ['node docker-exec/run-script.cjs'].filter(Boolean).join(' && '),
|
|
85
|
+
hookName: 'migration',
|
|
86
|
+
nodeInfo,
|
|
87
|
+
runScriptDir: path.join(__dirname, '../../migration-dist'),
|
|
88
|
+
runScriptParams: {
|
|
89
|
+
blocklet: b,
|
|
90
|
+
appDir: b.env.appDir,
|
|
91
|
+
env: nextEnv,
|
|
92
|
+
oldVersion: oldVersions[b.meta.did],
|
|
93
|
+
newVersion: b.meta.version,
|
|
94
|
+
...getHookArgs(b),
|
|
95
|
+
},
|
|
96
|
+
...hookArgs,
|
|
97
|
+
timeout: 1000 * 120,
|
|
98
|
+
retry: 0,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
await runBlockletMigrationScripts({
|
|
103
|
+
blocklet: b,
|
|
104
|
+
appDir: b.env.appDir,
|
|
105
|
+
env: nextEnv,
|
|
106
|
+
oldVersion: oldVersions[b.meta.did],
|
|
107
|
+
newVersion: b.meta.version,
|
|
108
|
+
...hookArgs,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
logger.error('Failed to run migration scripts', { appDid: did, title: b.meta.title, error });
|
|
113
|
+
// dev 模式下,migration 失败不抛出错误,只打印日志
|
|
114
|
+
if (process.env.ABT_NODE_DEV_MODE !== 'true') {
|
|
115
|
+
error.message = `Failed to run migration scripts for ${b.meta.title}: ${error.message}`;
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
await forEachComponentV2(blocklet, runMigration, {
|
|
121
|
+
parallel,
|
|
122
|
+
concurrencyLimit: parallel ? 4 : 1,
|
|
123
|
+
});
|
|
124
|
+
logger.info('done migration on upgrading', { did, componentDids });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Clean upload file
|
|
129
|
+
*/
|
|
130
|
+
function _cleanUploadFile(url) {
|
|
131
|
+
setTimeout(() => {
|
|
132
|
+
removeUploadFile(url);
|
|
133
|
+
}, 10000); // 10s 后删除上传的文件
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Ensure deleted children in settings
|
|
138
|
+
*/
|
|
139
|
+
async function _ensureDeletedChildrenInSettings(manager, blocklet) {
|
|
140
|
+
const { did } = blocklet.meta;
|
|
141
|
+
|
|
142
|
+
// TODO 不从 settings 中取值, 直接存在 extra 中
|
|
143
|
+
let deletedChildren = await states.blockletExtras.getSettings(did, 'children', []);
|
|
144
|
+
deletedChildren = deletedChildren.filter(
|
|
145
|
+
(x) => x.status === BlockletStatus.deleted && !blocklet.children.some((y) => y.meta.did === x.meta.did)
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
await states.blockletExtras.setSettings(did, { children: deletedChildren });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Upgrade blocklet
|
|
153
|
+
*/
|
|
154
|
+
async function _upgradeBlocklet(
|
|
155
|
+
manager,
|
|
156
|
+
{ newBlocklet, oldBlocklet, componentDids, action, context = {}, shouldCleanUploadFile, url }
|
|
157
|
+
) {
|
|
158
|
+
const { meta, source, deployedFrom, children } = newBlocklet;
|
|
159
|
+
const { did, version, name } = meta;
|
|
160
|
+
const title = getDisplayName(newBlocklet);
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
// delete old process
|
|
164
|
+
try {
|
|
165
|
+
await manager.deleteProcess({ did, componentDids }, context);
|
|
166
|
+
logger.info('delete blocklet process for upgrading', { did, componentDids });
|
|
167
|
+
} catch (err) {
|
|
168
|
+
logger.error('delete blocklet process for upgrading', { did, componentDids, error: err });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// update state
|
|
172
|
+
await states.blocklet.upgradeBlocklet({ meta, source, deployedFrom, children });
|
|
173
|
+
logger.info('updated blocklet for upgrading', { did, componentDids, source, name });
|
|
174
|
+
// ensure component status is upgrading
|
|
175
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading, { componentDids });
|
|
176
|
+
await manager._setConfigsFromMeta(did);
|
|
177
|
+
|
|
178
|
+
// should ensure blocklet integrity
|
|
179
|
+
let blocklet = await manager.ensureBlocklet(did);
|
|
180
|
+
|
|
181
|
+
// Add environments
|
|
182
|
+
await manager._updateBlockletEnvironment(did);
|
|
183
|
+
blocklet = await manager.getBlocklet(did);
|
|
184
|
+
|
|
185
|
+
const nodeInfo = await states.node.read();
|
|
186
|
+
const nodeEnvironments = await states.node.getEnvironments(nodeInfo);
|
|
187
|
+
const env = getRuntimeEnvironments(blocklet, nodeEnvironments, {});
|
|
188
|
+
const needRunDocker = await checkNeedRunDocker(blocklet.meta, env, nodeInfo, isExternalBlocklet(blocklet));
|
|
189
|
+
|
|
190
|
+
await manager._runUserHook('preInstall', blocklet, context);
|
|
191
|
+
// post install
|
|
192
|
+
await manager._runUserHook('postInstall', blocklet, context);
|
|
193
|
+
|
|
194
|
+
await manager._runUserHook('preFlight', blocklet, context);
|
|
195
|
+
|
|
196
|
+
await _runMigration(manager, { parallel: !needRunDocker, did, blocklet, oldBlocklet, componentDids });
|
|
197
|
+
|
|
198
|
+
// handle component status
|
|
199
|
+
const runningDids = [];
|
|
200
|
+
const stoppedDids = [];
|
|
201
|
+
if (action === INSTALL_ACTIONS.INSTALL_COMPONENT) {
|
|
202
|
+
for (const componentDid of componentDids) {
|
|
203
|
+
const component = blocklet.children.find((x) => x.meta.did === componentDid);
|
|
204
|
+
if (hasStartEngine(component.meta)) {
|
|
205
|
+
// NOT start immediately for runnable component
|
|
206
|
+
stoppedDids.push(componentDid);
|
|
207
|
+
} else {
|
|
208
|
+
// Start immediately for resource component
|
|
209
|
+
runningDids.push(componentDid);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
for (const componentDid of componentDids) {
|
|
214
|
+
const old = oldBlocklet.children.find((x) => x.meta.did === componentDid);
|
|
215
|
+
if (old?.status === BlockletStatus.running) {
|
|
216
|
+
runningDids.push(componentDid);
|
|
217
|
+
} else {
|
|
218
|
+
stoppedDids.push(componentDid);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (runningDids.length) {
|
|
223
|
+
const initialized = !!blocklet.settings?.initialized;
|
|
224
|
+
if (initialized) {
|
|
225
|
+
try {
|
|
226
|
+
await manager.start({ did, componentDids: runningDids }, context);
|
|
227
|
+
logger.info('started blocklet for upgrading', { did, version, runningDids });
|
|
228
|
+
} catch (error) {
|
|
229
|
+
logger.error('failed to start blocklet for upgrading', { did, version, runningDids, error });
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids: runningDids });
|
|
233
|
+
logger.info('set blocklet as stopped since not initialized', { did, version, runningDids });
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (stoppedDids.length) {
|
|
237
|
+
const status = action === INSTALL_ACTIONS.INSTALL_COMPONENT ? BlockletStatus.installed : BlockletStatus.stopped;
|
|
238
|
+
await states.blocklet.setBlockletStatus(did, status, { componentDids: stoppedDids });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
blocklet = await manager.getBlocklet(did, context);
|
|
242
|
+
|
|
243
|
+
await updateBlockletFallbackLogo(blocklet);
|
|
244
|
+
|
|
245
|
+
await manager._updateDependents(did);
|
|
246
|
+
|
|
247
|
+
manager.refreshListCache();
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
manager.emit(BlockletEvents.upgraded, { blocklet, context }); // trigger router refresh
|
|
251
|
+
|
|
252
|
+
const isInstallAction = action === INSTALL_ACTIONS.INSTALL_COMPONENT;
|
|
253
|
+
const notificationEvent = isInstallAction ? BlockletEvents.componentInstalled : BlockletEvents.componentUpgraded;
|
|
254
|
+
const actionName = isInstallAction ? 'installed' : 'upgraded';
|
|
255
|
+
|
|
256
|
+
manager.emit(notificationEvent, { ...blocklet, componentDids, oldBlocklet, context });
|
|
257
|
+
|
|
258
|
+
const upgradeLength = componentDids?.length || 0;
|
|
259
|
+
|
|
260
|
+
manager._createNotification(did, {
|
|
261
|
+
title: `${upgradeLength > 1 ? `${upgradeLength} components` : 'Component'} ${actionName} successfully for ${title}`,
|
|
262
|
+
description: `${getComponentNamesWithVersion(
|
|
263
|
+
newBlocklet,
|
|
264
|
+
componentDids
|
|
265
|
+
)} is ${actionName} successfully for ${title}`,
|
|
266
|
+
action: `/blocklets/${did}/overview`,
|
|
267
|
+
blockletDashboardAction: `${WELLKNOWN_SERVICE_PATH_PREFIX}/admin/blocklets`,
|
|
268
|
+
entityType: 'blocklet',
|
|
269
|
+
entityId: did,
|
|
270
|
+
severity: 'success',
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
if (shouldCleanUploadFile && url) {
|
|
274
|
+
_cleanUploadFile(url);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (isInstallAction && process.env.NODE_ENV !== 'test') {
|
|
278
|
+
manager.start({ did, componentDids });
|
|
279
|
+
}
|
|
280
|
+
} catch (error) {
|
|
281
|
+
logger.error('emit upgrade notification failed', { name, version, error });
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Update dynamic component meta in blocklet settings
|
|
285
|
+
await _ensureDeletedChildrenInSettings(manager, blocklet);
|
|
286
|
+
|
|
287
|
+
await manager._rollbackCache.remove({ did: blocklet.meta.did });
|
|
288
|
+
|
|
289
|
+
if (oldBlocklet.status === BlockletStatus.running) {
|
|
290
|
+
manager.emit(
|
|
291
|
+
action === INSTALL_ACTIONS.INSTALL_COMPONENT
|
|
292
|
+
? BlockletInternalEvents.componentInstalled
|
|
293
|
+
: BlockletInternalEvents.componentUpgraded,
|
|
294
|
+
{
|
|
295
|
+
appDid: blocklet.appDid,
|
|
296
|
+
components: getComponentsInternalInfo(blocklet).filter((c) => componentDids.includes(c.did)),
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
manager.configSynchronizer.throttledSyncAppConfig(blocklet);
|
|
301
|
+
|
|
302
|
+
return blocklet;
|
|
303
|
+
} catch (err) {
|
|
304
|
+
const b = await manager._rollback(action, did, oldBlocklet);
|
|
305
|
+
logger.error('failed to upgrade blocklet', { did, version, name, error: err });
|
|
306
|
+
|
|
307
|
+
manager.emit(BlockletEvents.updated, { ...b, context });
|
|
308
|
+
|
|
309
|
+
const actionName = action === INSTALL_ACTIONS.INSTALL_COMPONENT ? 'install' : 'upgrade';
|
|
310
|
+
const notificationEvent =
|
|
311
|
+
action === INSTALL_ACTIONS.INSTALL_COMPONENT
|
|
312
|
+
? BlockletEvents.componentInstallFailed
|
|
313
|
+
: BlockletEvents.componentUpgradeFailed;
|
|
314
|
+
|
|
315
|
+
manager.emit(notificationEvent, {
|
|
316
|
+
blocklet: { ...newBlocklet, componentDids, error: { message: err.message } },
|
|
317
|
+
context,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
const upgradeLength = componentDids?.length || 0;
|
|
321
|
+
manager._createNotification(did, {
|
|
322
|
+
title: `${upgradeLength > 1 ? `${upgradeLength} components` : 'Component'} failed to ${actionName} for ${title}`,
|
|
323
|
+
description: `${getComponentNamesWithVersion(newBlocklet, componentDids)} ${actionName} failed for ${title}: ${
|
|
324
|
+
err.message
|
|
325
|
+
}.`,
|
|
326
|
+
entityType: 'blocklet',
|
|
327
|
+
entityId: did,
|
|
328
|
+
severity: 'error',
|
|
329
|
+
});
|
|
330
|
+
throw err;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
module.exports = {
|
|
335
|
+
_onRestart,
|
|
336
|
+
_runMigration,
|
|
337
|
+
_cleanUploadFile,
|
|
338
|
+
_ensureDeletedChildrenInSettings,
|
|
339
|
+
_upgradeBlocklet,
|
|
340
|
+
};
|