@abtnode/core 1.16.52-beta-20251003-083412-fdfc4e36 → 1.16.52-beta-20251005-235515-42ad5caf
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.js +579 -47
- package/lib/blocklet/downloader/blocklet-downloader.js +2 -2
- package/lib/blocklet/downloader/bundle-downloader.js +13 -38
- package/lib/blocklet/manager/disk.js +200 -84
- package/lib/blocklet/manager/ensure-blocklet-running.js +3 -2
- package/lib/blocklet/manager/helper/blue-green-get-componentids.js +59 -0
- package/lib/blocklet/manager/helper/blue-green-start-blocklet.js +292 -0
- package/lib/blocklet/manager/helper/blue-green-update-blocklet-status.js +18 -0
- package/lib/blocklet/manager/helper/blue-green-upgrade-blocklet.js +191 -0
- package/lib/blocklet/manager/helper/upgrade-components.js +2 -9
- package/lib/blocklet/migration-dist/migration.cjs +458 -456
- package/lib/blocklet/passport/index.js +8 -2
- package/lib/index.js +18 -0
- package/lib/monitor/blocklet-runtime-monitor.js +12 -7
- package/lib/states/audit-log.js +54 -2
- package/lib/states/blocklet.js +23 -8
- package/lib/states/index.js +3 -0
- package/lib/states/org.js +661 -0
- package/lib/team/manager.js +10 -3
- package/lib/util/blocklet.js +190 -115
- package/lib/util/docker/is-docker-only-single-instances.js +17 -0
- package/lib/util/org.js +99 -0
- package/lib/validators/org.js +19 -0
- package/package.json +26 -26
|
@@ -73,6 +73,7 @@ const { encrypt } = require('@blocklet/sdk/lib/security');
|
|
|
73
73
|
const { generateRandomString } = require('@abtnode/models/lib/util');
|
|
74
74
|
const { getComponentApiKey, getBlockletLogos } = require('@abtnode/util/lib/blocklet');
|
|
75
75
|
const { getEmailServiceProvider, emailConfigSchema } = require('@abtnode/auth/lib/email');
|
|
76
|
+
const { Joi } = require('@arcblock/validator');
|
|
76
77
|
|
|
77
78
|
const {
|
|
78
79
|
BlockletStatus,
|
|
@@ -134,6 +135,9 @@ const {
|
|
|
134
135
|
refresh: refreshAccessibleExternalNodeIp,
|
|
135
136
|
getFromCache: getAccessibleExternalNodeIp,
|
|
136
137
|
} = require('../../util/get-accessible-external-node-ip');
|
|
138
|
+
const { blueGreenStartBlocklet } = require('./helper/blue-green-start-blocklet.js');
|
|
139
|
+
const { blueGreenUpgradeBlocklet } = require('./helper/blue-green-upgrade-blocklet.js');
|
|
140
|
+
|
|
137
141
|
const {
|
|
138
142
|
getAppSystemEnvironments,
|
|
139
143
|
getComponentSystemEnvironments,
|
|
@@ -175,6 +179,7 @@ const {
|
|
|
175
179
|
getPackConfig,
|
|
176
180
|
copyPackImages,
|
|
177
181
|
filterRequiredComponents,
|
|
182
|
+
getHookArgs,
|
|
178
183
|
} = require('../../util/blocklet');
|
|
179
184
|
const states = require('../../states');
|
|
180
185
|
const BaseBlockletManager = require('./base');
|
|
@@ -246,6 +251,7 @@ const { blockletThemeSchema } = require('../../validators/theme');
|
|
|
246
251
|
const { removeDockerNetwork } = require('../../util/docker/docker-network.js');
|
|
247
252
|
const parseDockerName = require('../../util/docker/parse-docker-name.js');
|
|
248
253
|
const { verifyAigneConfig, decryptValue } = require('../../util/aigne-verify');
|
|
254
|
+
const { blueGreenGetComponentIds } = require('./helper/blue-green-get-componentids.js');
|
|
249
255
|
|
|
250
256
|
const { formatEnvironments, getBlockletMeta, validateOwner, isCLI } = util;
|
|
251
257
|
|
|
@@ -271,16 +277,6 @@ const USER_PROFILE_SYNC_FIELDS = [
|
|
|
271
277
|
'phoneVerified',
|
|
272
278
|
];
|
|
273
279
|
|
|
274
|
-
const getHookArgs = (blocklet) => ({
|
|
275
|
-
output: blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? '' : path.join(blocklet.env.logsDir, 'output.log'),
|
|
276
|
-
error: blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? '' : path.join(blocklet.env.logsDir, 'error.log'),
|
|
277
|
-
timeout:
|
|
278
|
-
Math.max(
|
|
279
|
-
get(blocklet, 'meta.timeout.script', 120),
|
|
280
|
-
...(blocklet?.children || []).map((child) => child.meta?.timeout?.script || 0)
|
|
281
|
-
) * 1000,
|
|
282
|
-
});
|
|
283
|
-
|
|
284
280
|
const pm2StatusMap = {
|
|
285
281
|
online: BlockletStatus.running,
|
|
286
282
|
stop: BlockletStatus.stopped,
|
|
@@ -730,7 +726,8 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
730
726
|
}
|
|
731
727
|
|
|
732
728
|
async checkComponentsForUpdates({ did }) {
|
|
733
|
-
|
|
729
|
+
const blocklet = await this.ensureBlocklet(did);
|
|
730
|
+
await this.checkControllerStatus(blocklet, 'upgrade');
|
|
734
731
|
return UpgradeComponents.check({ did, states });
|
|
735
732
|
}
|
|
736
733
|
|
|
@@ -876,6 +873,12 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
876
873
|
) {
|
|
877
874
|
const operator = _operator || context?.user?.did;
|
|
878
875
|
|
|
876
|
+
try {
|
|
877
|
+
await this.deleteProcess({ did, componentDids, isGreen: true });
|
|
878
|
+
} catch {
|
|
879
|
+
// do nothing
|
|
880
|
+
}
|
|
881
|
+
|
|
879
882
|
logger.info('start blocklet', {
|
|
880
883
|
did: did || inputBlocklet.meta.did,
|
|
881
884
|
componentDids,
|
|
@@ -1047,7 +1050,10 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1047
1050
|
});
|
|
1048
1051
|
|
|
1049
1052
|
// check blocklet healthy
|
|
1050
|
-
const { startTimeout, minConsecutiveTime } = getHealthyCheckTimeout(blocklet, {
|
|
1053
|
+
const { startTimeout, minConsecutiveTime } = getHealthyCheckTimeout(blocklet, {
|
|
1054
|
+
checkHealthImmediately,
|
|
1055
|
+
componentDids,
|
|
1056
|
+
});
|
|
1051
1057
|
const params = {
|
|
1052
1058
|
did,
|
|
1053
1059
|
context,
|
|
@@ -1117,7 +1123,11 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1117
1123
|
const { processId } = blocklet.env;
|
|
1118
1124
|
|
|
1119
1125
|
if (updateStatus) {
|
|
1120
|
-
const doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping, {
|
|
1126
|
+
const doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping, {
|
|
1127
|
+
componentDids,
|
|
1128
|
+
operator,
|
|
1129
|
+
isGreenAndBlue: true,
|
|
1130
|
+
});
|
|
1121
1131
|
blocklet.status = BlockletStatus.stopping;
|
|
1122
1132
|
this.emit(BlockletEvents.statusChange, doc);
|
|
1123
1133
|
}
|
|
@@ -1159,11 +1169,16 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1159
1169
|
});
|
|
1160
1170
|
},
|
|
1161
1171
|
componentDids,
|
|
1172
|
+
isStopGreenAndBlue: true,
|
|
1162
1173
|
});
|
|
1163
1174
|
} catch (error) {
|
|
1164
1175
|
logger.error('Failed to stop blocklet', { error, did });
|
|
1165
1176
|
if (updateStatus) {
|
|
1166
|
-
const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, {
|
|
1177
|
+
const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, {
|
|
1178
|
+
componentDids,
|
|
1179
|
+
operator,
|
|
1180
|
+
isGreenAndBlue: true,
|
|
1181
|
+
});
|
|
1167
1182
|
this.emit(BlockletEvents.statusChange, res);
|
|
1168
1183
|
}
|
|
1169
1184
|
throw error;
|
|
@@ -1174,7 +1189,11 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1174
1189
|
launcher.notifyBlockletStopped(blocklet);
|
|
1175
1190
|
|
|
1176
1191
|
if (updateStatus) {
|
|
1177
|
-
const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, {
|
|
1192
|
+
const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, {
|
|
1193
|
+
componentDids,
|
|
1194
|
+
operator,
|
|
1195
|
+
isGreenAndBlue: true,
|
|
1196
|
+
});
|
|
1178
1197
|
// send notification to websocket channel
|
|
1179
1198
|
this.emit(BlockletEvents.statusChange, res);
|
|
1180
1199
|
|
|
@@ -1286,10 +1305,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1286
1305
|
logger.info('restart blocklet', { did, operator });
|
|
1287
1306
|
const blocklet = await this.getBlocklet(did);
|
|
1288
1307
|
await this.checkControllerStatus(blocklet, 'restart');
|
|
1289
|
-
|
|
1290
|
-
componentDids,
|
|
1291
|
-
operator,
|
|
1292
|
-
});
|
|
1308
|
+
|
|
1293
1309
|
const result = await states.blocklet.getBlocklet(did);
|
|
1294
1310
|
this.emit(BlockletEvents.statusChange, result);
|
|
1295
1311
|
|
|
@@ -1302,12 +1318,9 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1302
1318
|
operator,
|
|
1303
1319
|
context,
|
|
1304
1320
|
});
|
|
1305
|
-
ticket.on('failed',
|
|
1321
|
+
ticket.on('failed', (err) => {
|
|
1306
1322
|
logger.error('failed to restart blocklet', { did, error: err });
|
|
1307
1323
|
|
|
1308
|
-
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids, operator });
|
|
1309
|
-
this.emit(BlockletEvents.statusChange, state);
|
|
1310
|
-
|
|
1311
1324
|
const description = `${getComponentNamesWithVersion(result, componentDids)} restart failed for ${getDisplayName(
|
|
1312
1325
|
result
|
|
1313
1326
|
)}: ${err.message || 'queue exception'}`;
|
|
@@ -1640,20 +1653,23 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1640
1653
|
|
|
1641
1654
|
const { name, did, version } = blocklet.meta;
|
|
1642
1655
|
|
|
1643
|
-
if (
|
|
1656
|
+
if (
|
|
1657
|
+
![BlockletStatus.downloading, BlockletStatus.waiting].includes(blocklet.greenStatus) &&
|
|
1658
|
+
![BlockletStatus.downloading, BlockletStatus.waiting].includes(blocklet.status)
|
|
1659
|
+
) {
|
|
1644
1660
|
throw new Error(`Can not cancel blocklet that status is ${fromBlockletStatus(blocklet.status)}`);
|
|
1645
1661
|
}
|
|
1646
1662
|
|
|
1647
1663
|
const job = await this.installQueue.get(did);
|
|
1648
1664
|
|
|
1649
1665
|
// cancel job
|
|
1650
|
-
if (blocklet.status === BlockletStatus.downloading) {
|
|
1666
|
+
if (blocklet.greenStatus === BlockletStatus.downloading || blocklet.status === BlockletStatus.downloading) {
|
|
1651
1667
|
try {
|
|
1652
1668
|
await this.blockletDownloader.cancelDownload(blocklet.meta.did);
|
|
1653
1669
|
} catch (error) {
|
|
1654
1670
|
logger.error('failed to exec blockletDownloader.download', { did: blocklet.meta.did, error });
|
|
1655
1671
|
}
|
|
1656
|
-
} else if (blocklet.status === BlockletStatus.waiting) {
|
|
1672
|
+
} else if (blocklet.greenStatus === BlockletStatus.waiting || blocklet.status === BlockletStatus.waiting) {
|
|
1657
1673
|
try {
|
|
1658
1674
|
await this.installQueue.cancel(blocklet.meta.did);
|
|
1659
1675
|
} catch (error) {
|
|
@@ -1670,7 +1686,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1670
1686
|
if (data) {
|
|
1671
1687
|
const { action, oldBlocklet } = data;
|
|
1672
1688
|
await this._rollback(action, did, oldBlocklet);
|
|
1673
|
-
await this._rollbackCache.remove({ did });
|
|
1689
|
+
await this._rollbackCache.remove({ did: inputDid });
|
|
1674
1690
|
} else {
|
|
1675
1691
|
throw new Error(`Cannot find rollback data in queue or backup file of blocklet ${inputDid}`);
|
|
1676
1692
|
}
|
|
@@ -1686,7 +1702,10 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1686
1702
|
if (blocklet) {
|
|
1687
1703
|
const componentDids = [];
|
|
1688
1704
|
forEachComponentV2Sync(blocklet, (x) => {
|
|
1689
|
-
if (
|
|
1705
|
+
if (
|
|
1706
|
+
[BlockletStatus.waiting, BlockletStatus.downloading].includes(x.status) ||
|
|
1707
|
+
[BlockletStatus.waiting, BlockletStatus.downloading].includes(x.greenStatus)
|
|
1708
|
+
) {
|
|
1690
1709
|
componentDids.push(x.meta.did);
|
|
1691
1710
|
}
|
|
1692
1711
|
});
|
|
@@ -1712,23 +1731,23 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1712
1731
|
* @param {*} context
|
|
1713
1732
|
* @returns
|
|
1714
1733
|
*/
|
|
1715
|
-
async
|
|
1734
|
+
deleteProcess = async ({ did, componentDids, shouldUpdateBlockletStatus = true, isGreen = false }, context) => {
|
|
1716
1735
|
const blocklet = await this.getBlocklet(did);
|
|
1717
1736
|
|
|
1718
1737
|
logger.info('delete blocklet process', { did, componentDids });
|
|
1719
1738
|
|
|
1720
|
-
await deleteBlockletProcess(blocklet, { ...context, componentDids });
|
|
1739
|
+
await deleteBlockletProcess(blocklet, { ...context, componentDids, isGreen });
|
|
1721
1740
|
logger.info('blocklet process deleted successfully', { did, componentDids });
|
|
1722
1741
|
|
|
1723
1742
|
// 有些情况不需要更新 blocklet 状态, 比如下载完成,安装之前清理 process 时, 不需要更新 blocklet 状态
|
|
1724
1743
|
if (shouldUpdateBlockletStatus) {
|
|
1725
|
-
const result = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids });
|
|
1744
|
+
const result = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids, isGreen });
|
|
1726
1745
|
logger.info('blocklet status updated to stopped after deleted processes', { did, componentDids });
|
|
1727
1746
|
return result;
|
|
1728
1747
|
}
|
|
1729
1748
|
|
|
1730
1749
|
return blocklet;
|
|
1731
|
-
}
|
|
1750
|
+
};
|
|
1732
1751
|
|
|
1733
1752
|
// Get blocklet by blockletDid or appDid
|
|
1734
1753
|
async detail(
|
|
@@ -2907,7 +2926,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2907
2926
|
return summary;
|
|
2908
2927
|
}
|
|
2909
2928
|
|
|
2910
|
-
async updateBlockletSettings({ did, enableSessionHardening, invite, gateway, aigne }, context) {
|
|
2929
|
+
async updateBlockletSettings({ did, enableSessionHardening, invite, gateway, aigne, org }, context) {
|
|
2911
2930
|
const params = {};
|
|
2912
2931
|
if (!isNil(enableSessionHardening)) {
|
|
2913
2932
|
params.enableSessionHardening = enableSessionHardening;
|
|
@@ -2939,6 +2958,20 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2939
2958
|
url: getOriginUrl(aigne.url),
|
|
2940
2959
|
};
|
|
2941
2960
|
}
|
|
2961
|
+
|
|
2962
|
+
if (!isNil(org)) {
|
|
2963
|
+
const ORG_SCHEMA = Joi.object({
|
|
2964
|
+
enabled: Joi.boolean().required(),
|
|
2965
|
+
maxMemberPerOrg: Joi.number().required().min(2).default(100),
|
|
2966
|
+
maxOrgPerUser: Joi.number().required().min(1).default(10),
|
|
2967
|
+
});
|
|
2968
|
+
const { error } = ORG_SCHEMA.validate(org);
|
|
2969
|
+
if (error) {
|
|
2970
|
+
throw new CustomError(400, error.message);
|
|
2971
|
+
}
|
|
2972
|
+
params.org = org;
|
|
2973
|
+
}
|
|
2974
|
+
|
|
2942
2975
|
const keys = Object.keys(params);
|
|
2943
2976
|
if (!keys.length) {
|
|
2944
2977
|
throw new Error('No settings to update');
|
|
@@ -3208,7 +3241,22 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3208
3241
|
return x;
|
|
3209
3242
|
});
|
|
3210
3243
|
|
|
3244
|
+
const blueGreenComponentIds = await blueGreenGetComponentIds(oldBlocklet || blocklet, componentDids);
|
|
3245
|
+
|
|
3211
3246
|
try {
|
|
3247
|
+
await Promise.all(
|
|
3248
|
+
blueGreenComponentIds.map(async (item) => {
|
|
3249
|
+
if (item.componentDids.length === 0) {
|
|
3250
|
+
return;
|
|
3251
|
+
}
|
|
3252
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
|
|
3253
|
+
componentDids: item.componentDids,
|
|
3254
|
+
isGreen: item.changeToGreen,
|
|
3255
|
+
});
|
|
3256
|
+
})
|
|
3257
|
+
);
|
|
3258
|
+
const state = await states.blocklet.getBlocklet(did);
|
|
3259
|
+
this.emit(BlockletEvents.statusChange, state);
|
|
3212
3260
|
const { isCancelled } = await this._downloadBlocklet(
|
|
3213
3261
|
{
|
|
3214
3262
|
...blocklet,
|
|
@@ -3269,8 +3317,10 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3269
3317
|
|
|
3270
3318
|
// rollback on download failed
|
|
3271
3319
|
await statusLock.acquire('rollback-on-download-failed');
|
|
3320
|
+
|
|
3272
3321
|
try {
|
|
3273
|
-
|
|
3322
|
+
const status = await states.blocklet.getBlockletStatus(did);
|
|
3323
|
+
if ([BlockletStatus.downloading, BlockletStatus.waiting].includes(status)) {
|
|
3274
3324
|
await this._rollback(postAction, did, oldBlocklet);
|
|
3275
3325
|
}
|
|
3276
3326
|
} catch (error) {
|
|
@@ -3289,25 +3339,38 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3289
3339
|
// update status
|
|
3290
3340
|
await statusLock.acquire('download-update-status');
|
|
3291
3341
|
try {
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3342
|
+
await Promise.all(
|
|
3343
|
+
blueGreenComponentIds.map(async (item) => {
|
|
3344
|
+
if (item.componentDids.length === 0) {
|
|
3345
|
+
return;
|
|
3346
|
+
}
|
|
3347
|
+
if ((await states.blocklet.getBlockletStatus(did)) !== BlockletStatus.downloading) {
|
|
3348
|
+
throw new Error('blocklet status changed durning download');
|
|
3349
|
+
}
|
|
3295
3350
|
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3351
|
+
if (postAction === INSTALL_ACTIONS.INSTALL) {
|
|
3352
|
+
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.installing, {
|
|
3353
|
+
componentDids: item.componentDids,
|
|
3354
|
+
isGreen: item.changeToGreen,
|
|
3355
|
+
});
|
|
3356
|
+
this.emit(BlockletEvents.statusChange, state);
|
|
3357
|
+
}
|
|
3300
3358
|
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3359
|
+
if (
|
|
3360
|
+
[
|
|
3361
|
+
INSTALL_ACTIONS.INSTALL_COMPONENT,
|
|
3362
|
+
INSTALL_ACTIONS.UPGRADE_COMPONENT,
|
|
3363
|
+
'upgrade', // for backward compatibility
|
|
3364
|
+
].includes(postAction)
|
|
3365
|
+
) {
|
|
3366
|
+
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading, {
|
|
3367
|
+
componentDids: item.componentDids,
|
|
3368
|
+
isGreen: item.changeToGreen,
|
|
3369
|
+
});
|
|
3370
|
+
this.emit(BlockletEvents.statusChange, state);
|
|
3371
|
+
}
|
|
3372
|
+
})
|
|
3373
|
+
);
|
|
3311
3374
|
} catch (error) {
|
|
3312
3375
|
logger.error(error.message);
|
|
3313
3376
|
} finally {
|
|
@@ -3335,15 +3398,30 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3335
3398
|
logger.info('do upgrade blocklet', { did, version, postAction, componentDids });
|
|
3336
3399
|
|
|
3337
3400
|
try {
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3401
|
+
if (postAction === INSTALL_ACTIONS.UPGRADE_COMPONENT) {
|
|
3402
|
+
await blueGreenUpgradeBlocklet(
|
|
3403
|
+
{
|
|
3404
|
+
newBlocklet: blocklet,
|
|
3405
|
+
oldBlocklet,
|
|
3406
|
+
componentDids,
|
|
3407
|
+
action: postAction,
|
|
3408
|
+
shouldCleanUploadFile,
|
|
3409
|
+
url,
|
|
3410
|
+
},
|
|
3411
|
+
context,
|
|
3412
|
+
this,
|
|
3413
|
+
states
|
|
3414
|
+
);
|
|
3415
|
+
} else {
|
|
3416
|
+
await this._upgradeBlocklet({
|
|
3417
|
+
newBlocklet: blocklet,
|
|
3418
|
+
oldBlocklet,
|
|
3419
|
+
componentDids,
|
|
3420
|
+
action: postAction,
|
|
3421
|
+
shouldCleanUploadFile,
|
|
3422
|
+
url,
|
|
3423
|
+
});
|
|
3424
|
+
}
|
|
3347
3425
|
|
|
3348
3426
|
const newBlocklet = await this.getBlocklet(did);
|
|
3349
3427
|
|
|
@@ -3402,11 +3480,13 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3402
3480
|
}
|
|
3403
3481
|
|
|
3404
3482
|
async _onRestart({ did, componentDids, context, operator }) {
|
|
3405
|
-
await
|
|
3406
|
-
await this.start({ did, componentDids, operator }, context);
|
|
3483
|
+
await blueGreenStartBlocklet({ did, componentDids, operator }, context, this, states);
|
|
3407
3484
|
}
|
|
3408
3485
|
|
|
3409
|
-
|
|
3486
|
+
_onCheckIfStarted = async (
|
|
3487
|
+
jobInfo,
|
|
3488
|
+
{ throwOnError, skipRunningCheck = false, isGreen = false, needUpdateBlueStatus = true } = {}
|
|
3489
|
+
) => {
|
|
3410
3490
|
const startedAt = Date.now();
|
|
3411
3491
|
const { did, context, minConsecutiveTime = 2000, timeout, componentDids } = jobInfo;
|
|
3412
3492
|
const blocklet = await this.getBlocklet(did);
|
|
@@ -3415,20 +3495,29 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3415
3495
|
const { name } = meta;
|
|
3416
3496
|
|
|
3417
3497
|
const nodeInfo = await states.node.read();
|
|
3498
|
+
const successBlockletIds = new Set();
|
|
3418
3499
|
try {
|
|
3419
3500
|
if (!skipRunningCheck) {
|
|
3420
3501
|
await checkBlockletProcessHealthy(blocklet, {
|
|
3421
3502
|
minConsecutiveTime,
|
|
3422
3503
|
timeout,
|
|
3423
3504
|
componentDids,
|
|
3505
|
+
isGreen,
|
|
3424
3506
|
enableDocker: nodeInfo.enableDocker,
|
|
3425
3507
|
setBlockletRunning: async (componentDid) => {
|
|
3426
|
-
|
|
3508
|
+
successBlockletIds.add(componentDid);
|
|
3509
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
|
|
3510
|
+
componentDids: [componentDid],
|
|
3511
|
+
isGreen,
|
|
3512
|
+
});
|
|
3427
3513
|
},
|
|
3428
3514
|
});
|
|
3429
3515
|
}
|
|
3430
3516
|
|
|
3431
|
-
const runningRes = await states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
|
|
3517
|
+
const runningRes = await states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
|
|
3518
|
+
componentDids: successBlockletIds.size > 0 ? Array.from(successBlockletIds) : componentDids,
|
|
3519
|
+
isGreen,
|
|
3520
|
+
});
|
|
3432
3521
|
|
|
3433
3522
|
const res = await this.getBlocklet(did);
|
|
3434
3523
|
|
|
@@ -3445,8 +3534,30 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3445
3534
|
|
|
3446
3535
|
logger.info('blocklet healthy', { did, name, time: Date.now() - startedAt });
|
|
3447
3536
|
|
|
3537
|
+
if (needUpdateBlueStatus) {
|
|
3538
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, {
|
|
3539
|
+
componentDids,
|
|
3540
|
+
isGreen: !isGreen,
|
|
3541
|
+
});
|
|
3542
|
+
try {
|
|
3543
|
+
await this.deleteProcess({ did, componentDids, isGreen: !isGreen }, context);
|
|
3544
|
+
} catch {
|
|
3545
|
+
logger.error('delete process failed', { did, componentDids, isGreen });
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
|
|
3448
3549
|
return runningRes;
|
|
3449
3550
|
} catch (error) {
|
|
3551
|
+
const errorBlockletIds = [];
|
|
3552
|
+
for (const componentDid of componentDids) {
|
|
3553
|
+
if (!successBlockletIds.has(componentDid)) {
|
|
3554
|
+
errorBlockletIds.push(componentDid);
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
if (errorBlockletIds.length === 0) {
|
|
3558
|
+
return this.getBlocklet(did);
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3450
3561
|
const status = await states.blocklet.getBlockletStatus(did);
|
|
3451
3562
|
if ([BlockletStatus.stopping, BlockletStatus.stopped].includes(status)) {
|
|
3452
3563
|
logger.info(`Check blocklet healthy failing because blocklet is ${fromBlockletStatus(status)}`);
|
|
@@ -3455,12 +3566,21 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3455
3566
|
|
|
3456
3567
|
logger.error('check blocklet if started failed', { did, name, context, timeout, error });
|
|
3457
3568
|
|
|
3458
|
-
await this.deleteProcess({ did, componentDids }, context);
|
|
3459
|
-
const doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids });
|
|
3569
|
+
await this.deleteProcess({ did, componentDids: errorBlockletIds, isGreen }, context);
|
|
3460
3570
|
|
|
3461
|
-
|
|
3571
|
+
let doc;
|
|
3572
|
+
if (isGreen) {
|
|
3573
|
+
doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, {
|
|
3574
|
+
componentDids: errorBlockletIds,
|
|
3575
|
+
isGreen,
|
|
3576
|
+
});
|
|
3577
|
+
} else {
|
|
3578
|
+
doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids: errorBlockletIds });
|
|
3579
|
+
}
|
|
3580
|
+
|
|
3581
|
+
const componentNames = getComponentNamesWithVersion(blocklet, errorBlockletIds);
|
|
3462
3582
|
const description = componentNames
|
|
3463
|
-
? `${getComponentNamesWithVersion(blocklet,
|
|
3583
|
+
? `${getComponentNamesWithVersion(blocklet, errorBlockletIds)} start failed for ${getDisplayName(blocklet)}: ${
|
|
3464
3584
|
error.message
|
|
3465
3585
|
}`
|
|
3466
3586
|
: `${blocklet.meta.title} start failed: ${error.message}`;
|
|
@@ -3473,7 +3593,11 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3473
3593
|
severity: 'error',
|
|
3474
3594
|
});
|
|
3475
3595
|
|
|
3476
|
-
this.emit(BlockletEvents.startFailed, {
|
|
3596
|
+
this.emit(BlockletEvents.startFailed, {
|
|
3597
|
+
...doc,
|
|
3598
|
+
componentDids: errorBlockletIds,
|
|
3599
|
+
error: { message: error.message },
|
|
3600
|
+
});
|
|
3477
3601
|
this.emit(BlockletEvents.statusChange, { ...doc, error });
|
|
3478
3602
|
|
|
3479
3603
|
if (throwOnError) {
|
|
@@ -3482,7 +3606,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3482
3606
|
|
|
3483
3607
|
return doc;
|
|
3484
3608
|
}
|
|
3485
|
-
}
|
|
3609
|
+
};
|
|
3486
3610
|
|
|
3487
3611
|
/**
|
|
3488
3612
|
* @param {{
|
|
@@ -4401,7 +4525,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
4401
4525
|
}
|
|
4402
4526
|
}
|
|
4403
4527
|
|
|
4404
|
-
async _runMigration({
|
|
4528
|
+
async _runMigration({ parallel, did, blocklet, oldBlocklet, componentDids }) {
|
|
4405
4529
|
logger.info('start migration on upgrading', { did, componentDids });
|
|
4406
4530
|
const oldVersions = {};
|
|
4407
4531
|
forEachComponentV2Sync(oldBlocklet, (b) => {
|
|
@@ -4471,8 +4595,8 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
4471
4595
|
}
|
|
4472
4596
|
};
|
|
4473
4597
|
await forEachComponentV2(blocklet, runMigration, {
|
|
4474
|
-
parallel
|
|
4475
|
-
concurrencyLimit:
|
|
4598
|
+
parallel,
|
|
4599
|
+
concurrencyLimit: parallel ? 4 : 1,
|
|
4476
4600
|
});
|
|
4477
4601
|
logger.info('done migration on upgrading', { did, componentDids });
|
|
4478
4602
|
}
|
|
@@ -4530,7 +4654,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
4530
4654
|
|
|
4531
4655
|
await this._runUserHook('preFlight', blocklet, context);
|
|
4532
4656
|
|
|
4533
|
-
await this._runMigration({ needRunDocker, did, blocklet, oldBlocklet, componentDids });
|
|
4657
|
+
await this._runMigration({ parallel: !needRunDocker, did, blocklet, oldBlocklet, componentDids });
|
|
4534
4658
|
|
|
4535
4659
|
// handle component status
|
|
4536
4660
|
const runningDids = [];
|
|
@@ -4696,15 +4820,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
4696
4820
|
|
|
4697
4821
|
return this.blockletDownloader.download(blocklet, {
|
|
4698
4822
|
...context,
|
|
4699
|
-
preDownload:
|
|
4700
|
-
// update children status
|
|
4701
|
-
if (downloadComponentIds?.length) {
|
|
4702
|
-
const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
|
|
4703
|
-
componentDids: downloadComponentIds,
|
|
4704
|
-
});
|
|
4705
|
-
this.emit(BlockletEvents.statusChange, blocklet1);
|
|
4706
|
-
}
|
|
4707
|
-
},
|
|
4823
|
+
// preDownload: () => {},
|
|
4708
4824
|
onProgress: (data) => {
|
|
4709
4825
|
this.emit(BlockletEvents.downloadBundleProgress, { appDid: appDid || did, meta: { did }, ...data });
|
|
4710
4826
|
},
|
|
@@ -177,7 +177,8 @@ class EnsureBlockletRunning {
|
|
|
177
177
|
const { did } = rootBlocklet.meta;
|
|
178
178
|
if (rootBlocklet.children) {
|
|
179
179
|
for (const childBlocklet of rootBlocklet.children) {
|
|
180
|
-
const isRunning =
|
|
180
|
+
const isRunning =
|
|
181
|
+
runningStatuses.includes(childBlocklet.status) || childBlocklet.greenStatus === BlockletStatus.running;
|
|
181
182
|
const isInProgress = inProgressStatuses.includes(childBlocklet.status);
|
|
182
183
|
if (isRunning || isInProgress) {
|
|
183
184
|
if (!this.runningBlocklets[did]) {
|
|
@@ -207,7 +208,7 @@ class EnsureBlockletRunning {
|
|
|
207
208
|
return async () => {
|
|
208
209
|
if (!shouldCheckHealthy(blocklet)) {
|
|
209
210
|
// 如果 blocklet 是不需要启动的,并且不是 running,则设置为 running 状态
|
|
210
|
-
if (blocklet.status !== BlockletStatus.running) {
|
|
211
|
+
if (blocklet.status !== BlockletStatus.running && blocklet.greenStatus !== BlockletStatus.running) {
|
|
211
212
|
await this.states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
|
|
212
213
|
componentDids: [blocklet.meta.did],
|
|
213
214
|
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const { BlockletStatus } = require('../../../states/blocklet');
|
|
2
|
+
const { forEachBlocklet } = require('../../../util/blocklet');
|
|
3
|
+
|
|
4
|
+
const blueGreenGetComponentIds = async (blocklet, componentDids) => {
|
|
5
|
+
if (!blocklet) {
|
|
6
|
+
return {
|
|
7
|
+
componentDids: componentDids || [],
|
|
8
|
+
changeToGreen: false,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const componentDidsSet = new Set(componentDids);
|
|
12
|
+
const runningStatuses = new Set([BlockletStatus.running]);
|
|
13
|
+
const greenComponentIds = [];
|
|
14
|
+
const blueComponentIds = [];
|
|
15
|
+
|
|
16
|
+
const activeStatuses = new Set([BlockletStatus.running, BlockletStatus.starting]);
|
|
17
|
+
const children = blocklet.children || [];
|
|
18
|
+
const hasActiveInstance =
|
|
19
|
+
activeStatuses.has(blocklet.status) ||
|
|
20
|
+
activeStatuses.has(blocklet.greenStatus) ||
|
|
21
|
+
children.some((child) => activeStatuses.has(child.status) || activeStatuses.has(child.greenStatus));
|
|
22
|
+
|
|
23
|
+
if (!hasActiveInstance) {
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
componentDids: Array.from(componentDidsSet),
|
|
27
|
+
changeToGreen: false,
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
await forEachBlocklet(
|
|
33
|
+
blocklet,
|
|
34
|
+
(b) => {
|
|
35
|
+
if (!componentDidsSet.has(b.meta.did)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (runningStatuses.has(b.greenStatus)) {
|
|
39
|
+
greenComponentIds.push(b.meta.did);
|
|
40
|
+
} else {
|
|
41
|
+
blueComponentIds.push(b.meta.did);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{ parallel: true, concurrencyLimit: 10 }
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return [
|
|
48
|
+
{
|
|
49
|
+
componentDids: greenComponentIds,
|
|
50
|
+
changeToGreen: false,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
componentDids: blueComponentIds,
|
|
54
|
+
changeToGreen: true,
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
module.exports = { blueGreenGetComponentIds };
|