@abtnode/core 1.16.52-beta-20251003-083412-fdfc4e36 → 1.16.52-beta-20251008-091027-c46c73e3
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 +593 -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 +219 -89
- 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 +301 -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 +4 -1
- 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 +22 -6
- package/lib/states/index.js +3 -0
- package/lib/states/org.js +663 -0
- package/lib/team/manager.js +10 -3
- package/lib/util/blocklet.js +217 -137
- 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 +27 -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,26 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2939
2958
|
url: getOriginUrl(aigne.url),
|
|
2940
2959
|
};
|
|
2941
2960
|
}
|
|
2961
|
+
|
|
2962
|
+
let shouldRotateSession = false;
|
|
2963
|
+
if (!isNil(org)) {
|
|
2964
|
+
const ORG_SCHEMA = Joi.object({
|
|
2965
|
+
enabled: Joi.boolean().required(),
|
|
2966
|
+
maxMemberPerOrg: Joi.number().required().min(2).default(100),
|
|
2967
|
+
maxOrgPerUser: Joi.number().required().min(1).default(10),
|
|
2968
|
+
});
|
|
2969
|
+
const { error } = ORG_SCHEMA.validate(org);
|
|
2970
|
+
if (error) {
|
|
2971
|
+
throw new CustomError(400, error.message);
|
|
2972
|
+
}
|
|
2973
|
+
const currentState = await this.getBlocklet(did, { useCache: true });
|
|
2974
|
+
const orgEnabled = get(currentState, 'settings.org.enabled', false);
|
|
2975
|
+
if (orgEnabled !== org.enabled) {
|
|
2976
|
+
shouldRotateSession = true;
|
|
2977
|
+
}
|
|
2978
|
+
params.org = org;
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2942
2981
|
const keys = Object.keys(params);
|
|
2943
2982
|
if (!keys.length) {
|
|
2944
2983
|
throw new Error('No settings to update');
|
|
@@ -2956,6 +2995,10 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2956
2995
|
this.emit(BlockletInternalEvents.appSettingChanged, { appDid: did });
|
|
2957
2996
|
this.emit(BlockletEvents.updated, { ...newState, context });
|
|
2958
2997
|
|
|
2998
|
+
if (shouldRotateSession) {
|
|
2999
|
+
this.teamAPI.rotateSessionKey({ teamDid: did });
|
|
3000
|
+
}
|
|
3001
|
+
|
|
2959
3002
|
return newState;
|
|
2960
3003
|
}
|
|
2961
3004
|
|
|
@@ -3208,7 +3251,22 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3208
3251
|
return x;
|
|
3209
3252
|
});
|
|
3210
3253
|
|
|
3254
|
+
const blueGreenComponentIds = await blueGreenGetComponentIds(oldBlocklet || blocklet, componentDids);
|
|
3255
|
+
|
|
3211
3256
|
try {
|
|
3257
|
+
await Promise.all(
|
|
3258
|
+
blueGreenComponentIds.map(async (item) => {
|
|
3259
|
+
if (item.componentDids.length === 0) {
|
|
3260
|
+
return;
|
|
3261
|
+
}
|
|
3262
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
|
|
3263
|
+
componentDids: item.componentDids,
|
|
3264
|
+
isGreen: item.changeToGreen,
|
|
3265
|
+
});
|
|
3266
|
+
})
|
|
3267
|
+
);
|
|
3268
|
+
const state = await states.blocklet.getBlocklet(did);
|
|
3269
|
+
this.emit(BlockletEvents.statusChange, state);
|
|
3212
3270
|
const { isCancelled } = await this._downloadBlocklet(
|
|
3213
3271
|
{
|
|
3214
3272
|
...blocklet,
|
|
@@ -3269,8 +3327,10 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3269
3327
|
|
|
3270
3328
|
// rollback on download failed
|
|
3271
3329
|
await statusLock.acquire('rollback-on-download-failed');
|
|
3330
|
+
|
|
3272
3331
|
try {
|
|
3273
|
-
|
|
3332
|
+
const status = await states.blocklet.getBlockletStatus(did);
|
|
3333
|
+
if ([BlockletStatus.downloading, BlockletStatus.waiting].includes(status)) {
|
|
3274
3334
|
await this._rollback(postAction, did, oldBlocklet);
|
|
3275
3335
|
}
|
|
3276
3336
|
} catch (error) {
|
|
@@ -3289,25 +3349,38 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3289
3349
|
// update status
|
|
3290
3350
|
await statusLock.acquire('download-update-status');
|
|
3291
3351
|
try {
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3352
|
+
await Promise.all(
|
|
3353
|
+
blueGreenComponentIds.map(async (item) => {
|
|
3354
|
+
if (item.componentDids.length === 0) {
|
|
3355
|
+
return;
|
|
3356
|
+
}
|
|
3357
|
+
if ((await states.blocklet.getBlockletStatus(did)) !== BlockletStatus.downloading) {
|
|
3358
|
+
throw new Error('blocklet status changed durning download');
|
|
3359
|
+
}
|
|
3295
3360
|
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3361
|
+
if (postAction === INSTALL_ACTIONS.INSTALL) {
|
|
3362
|
+
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.installing, {
|
|
3363
|
+
componentDids: item.componentDids,
|
|
3364
|
+
isGreen: item.changeToGreen,
|
|
3365
|
+
});
|
|
3366
|
+
this.emit(BlockletEvents.statusChange, state);
|
|
3367
|
+
}
|
|
3300
3368
|
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3369
|
+
if (
|
|
3370
|
+
[
|
|
3371
|
+
INSTALL_ACTIONS.INSTALL_COMPONENT,
|
|
3372
|
+
INSTALL_ACTIONS.UPGRADE_COMPONENT,
|
|
3373
|
+
'upgrade', // for backward compatibility
|
|
3374
|
+
].includes(postAction)
|
|
3375
|
+
) {
|
|
3376
|
+
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading, {
|
|
3377
|
+
componentDids: item.componentDids,
|
|
3378
|
+
isGreen: item.changeToGreen,
|
|
3379
|
+
});
|
|
3380
|
+
this.emit(BlockletEvents.statusChange, state);
|
|
3381
|
+
}
|
|
3382
|
+
})
|
|
3383
|
+
);
|
|
3311
3384
|
} catch (error) {
|
|
3312
3385
|
logger.error(error.message);
|
|
3313
3386
|
} finally {
|
|
@@ -3335,15 +3408,30 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3335
3408
|
logger.info('do upgrade blocklet', { did, version, postAction, componentDids });
|
|
3336
3409
|
|
|
3337
3410
|
try {
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3411
|
+
if (postAction === INSTALL_ACTIONS.UPGRADE_COMPONENT) {
|
|
3412
|
+
await blueGreenUpgradeBlocklet(
|
|
3413
|
+
{
|
|
3414
|
+
newBlocklet: blocklet,
|
|
3415
|
+
oldBlocklet,
|
|
3416
|
+
componentDids,
|
|
3417
|
+
action: postAction,
|
|
3418
|
+
shouldCleanUploadFile,
|
|
3419
|
+
url,
|
|
3420
|
+
},
|
|
3421
|
+
context,
|
|
3422
|
+
this,
|
|
3423
|
+
states
|
|
3424
|
+
);
|
|
3425
|
+
} else {
|
|
3426
|
+
await this._upgradeBlocklet({
|
|
3427
|
+
newBlocklet: blocklet,
|
|
3428
|
+
oldBlocklet,
|
|
3429
|
+
componentDids,
|
|
3430
|
+
action: postAction,
|
|
3431
|
+
shouldCleanUploadFile,
|
|
3432
|
+
url,
|
|
3433
|
+
});
|
|
3434
|
+
}
|
|
3347
3435
|
|
|
3348
3436
|
const newBlocklet = await this.getBlocklet(did);
|
|
3349
3437
|
|
|
@@ -3402,11 +3490,13 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3402
3490
|
}
|
|
3403
3491
|
|
|
3404
3492
|
async _onRestart({ did, componentDids, context, operator }) {
|
|
3405
|
-
await
|
|
3406
|
-
await this.start({ did, componentDids, operator }, context);
|
|
3493
|
+
await blueGreenStartBlocklet({ did, componentDids, operator }, context, this, states);
|
|
3407
3494
|
}
|
|
3408
3495
|
|
|
3409
|
-
|
|
3496
|
+
_onCheckIfStarted = async (
|
|
3497
|
+
jobInfo,
|
|
3498
|
+
{ throwOnError, skipRunningCheck = false, isGreen = false, needUpdateBlueStatus = true } = {}
|
|
3499
|
+
) => {
|
|
3410
3500
|
const startedAt = Date.now();
|
|
3411
3501
|
const { did, context, minConsecutiveTime = 2000, timeout, componentDids } = jobInfo;
|
|
3412
3502
|
const blocklet = await this.getBlocklet(did);
|
|
@@ -3415,38 +3505,73 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3415
3505
|
const { name } = meta;
|
|
3416
3506
|
|
|
3417
3507
|
const nodeInfo = await states.node.read();
|
|
3508
|
+
const successBlockletIds = new Set();
|
|
3418
3509
|
try {
|
|
3419
3510
|
if (!skipRunningCheck) {
|
|
3420
3511
|
await checkBlockletProcessHealthy(blocklet, {
|
|
3421
3512
|
minConsecutiveTime,
|
|
3422
3513
|
timeout,
|
|
3423
3514
|
componentDids,
|
|
3515
|
+
isGreen,
|
|
3424
3516
|
enableDocker: nodeInfo.enableDocker,
|
|
3425
3517
|
setBlockletRunning: async (componentDid) => {
|
|
3426
|
-
|
|
3518
|
+
successBlockletIds.add(componentDid);
|
|
3519
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
|
|
3520
|
+
componentDids: [componentDid],
|
|
3521
|
+
isGreen,
|
|
3522
|
+
});
|
|
3427
3523
|
},
|
|
3428
3524
|
});
|
|
3429
3525
|
}
|
|
3430
3526
|
|
|
3431
|
-
const runningRes = await states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
|
|
3527
|
+
const runningRes = await states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
|
|
3528
|
+
componentDids: successBlockletIds.size > 0 ? Array.from(successBlockletIds) : componentDids,
|
|
3529
|
+
isGreen,
|
|
3530
|
+
});
|
|
3432
3531
|
|
|
3433
|
-
|
|
3532
|
+
if (needUpdateBlueStatus) {
|
|
3533
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, {
|
|
3534
|
+
componentDids,
|
|
3535
|
+
isGreen: !isGreen,
|
|
3536
|
+
});
|
|
3537
|
+
}
|
|
3434
3538
|
|
|
3539
|
+
const nextBlocklet = await this.getBlocklet(did);
|
|
3540
|
+
|
|
3541
|
+
await this.configSynchronizer.throttledSyncAppConfig(nextBlocklet);
|
|
3542
|
+
const componentsInfo = getComponentsInternalInfo(nextBlocklet);
|
|
3435
3543
|
this.emit(BlockletInternalEvents.componentStarted, {
|
|
3436
3544
|
appDid: blocklet.appDid,
|
|
3437
|
-
components:
|
|
3545
|
+
components: componentsInfo,
|
|
3438
3546
|
});
|
|
3439
|
-
this.configSynchronizer.throttledSyncAppConfig(res);
|
|
3440
3547
|
|
|
3441
|
-
this.emit(BlockletEvents.statusChange,
|
|
3442
|
-
this.emit(BlockletEvents.started, { ...
|
|
3548
|
+
this.emit(BlockletEvents.statusChange, nextBlocklet);
|
|
3549
|
+
this.emit(BlockletEvents.started, { ...nextBlocklet, componentDids });
|
|
3443
3550
|
|
|
3444
3551
|
launcher.notifyBlockletStarted(blocklet);
|
|
3445
3552
|
|
|
3446
3553
|
logger.info('blocklet healthy', { did, name, time: Date.now() - startedAt });
|
|
3447
3554
|
|
|
3555
|
+
if (needUpdateBlueStatus) {
|
|
3556
|
+
try {
|
|
3557
|
+
await this.deleteProcess({ did, componentDids, isGreen: !isGreen }, context);
|
|
3558
|
+
} catch {
|
|
3559
|
+
logger.error('delete process failed', { did, componentDids, isGreen });
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3562
|
+
|
|
3448
3563
|
return runningRes;
|
|
3449
3564
|
} catch (error) {
|
|
3565
|
+
const errorBlockletIds = [];
|
|
3566
|
+
for (const componentDid of componentDids) {
|
|
3567
|
+
if (!successBlockletIds.has(componentDid)) {
|
|
3568
|
+
errorBlockletIds.push(componentDid);
|
|
3569
|
+
}
|
|
3570
|
+
}
|
|
3571
|
+
if (errorBlockletIds.length === 0) {
|
|
3572
|
+
return this.getBlocklet(did);
|
|
3573
|
+
}
|
|
3574
|
+
|
|
3450
3575
|
const status = await states.blocklet.getBlockletStatus(did);
|
|
3451
3576
|
if ([BlockletStatus.stopping, BlockletStatus.stopped].includes(status)) {
|
|
3452
3577
|
logger.info(`Check blocklet healthy failing because blocklet is ${fromBlockletStatus(status)}`);
|
|
@@ -3455,12 +3580,21 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3455
3580
|
|
|
3456
3581
|
logger.error('check blocklet if started failed', { did, name, context, timeout, error });
|
|
3457
3582
|
|
|
3458
|
-
await this.deleteProcess({ did, componentDids }, context);
|
|
3459
|
-
const doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids });
|
|
3583
|
+
await this.deleteProcess({ did, componentDids: errorBlockletIds, isGreen }, context);
|
|
3460
3584
|
|
|
3461
|
-
|
|
3585
|
+
let doc;
|
|
3586
|
+
if (isGreen) {
|
|
3587
|
+
doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, {
|
|
3588
|
+
componentDids: errorBlockletIds,
|
|
3589
|
+
isGreen,
|
|
3590
|
+
});
|
|
3591
|
+
} else {
|
|
3592
|
+
doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids: errorBlockletIds });
|
|
3593
|
+
}
|
|
3594
|
+
|
|
3595
|
+
const componentNames = getComponentNamesWithVersion(blocklet, errorBlockletIds);
|
|
3462
3596
|
const description = componentNames
|
|
3463
|
-
? `${getComponentNamesWithVersion(blocklet,
|
|
3597
|
+
? `${getComponentNamesWithVersion(blocklet, errorBlockletIds)} start failed for ${getDisplayName(blocklet)}: ${
|
|
3464
3598
|
error.message
|
|
3465
3599
|
}`
|
|
3466
3600
|
: `${blocklet.meta.title} start failed: ${error.message}`;
|
|
@@ -3473,7 +3607,11 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3473
3607
|
severity: 'error',
|
|
3474
3608
|
});
|
|
3475
3609
|
|
|
3476
|
-
this.emit(BlockletEvents.startFailed, {
|
|
3610
|
+
this.emit(BlockletEvents.startFailed, {
|
|
3611
|
+
...doc,
|
|
3612
|
+
componentDids: errorBlockletIds,
|
|
3613
|
+
error: { message: error.message },
|
|
3614
|
+
});
|
|
3477
3615
|
this.emit(BlockletEvents.statusChange, { ...doc, error });
|
|
3478
3616
|
|
|
3479
3617
|
if (throwOnError) {
|
|
@@ -3482,7 +3620,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3482
3620
|
|
|
3483
3621
|
return doc;
|
|
3484
3622
|
}
|
|
3485
|
-
}
|
|
3623
|
+
};
|
|
3486
3624
|
|
|
3487
3625
|
/**
|
|
3488
3626
|
* @param {{
|
|
@@ -4401,7 +4539,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
4401
4539
|
}
|
|
4402
4540
|
}
|
|
4403
4541
|
|
|
4404
|
-
async _runMigration({
|
|
4542
|
+
async _runMigration({ parallel, did, blocklet, oldBlocklet, componentDids }) {
|
|
4405
4543
|
logger.info('start migration on upgrading', { did, componentDids });
|
|
4406
4544
|
const oldVersions = {};
|
|
4407
4545
|
forEachComponentV2Sync(oldBlocklet, (b) => {
|
|
@@ -4471,8 +4609,8 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
4471
4609
|
}
|
|
4472
4610
|
};
|
|
4473
4611
|
await forEachComponentV2(blocklet, runMigration, {
|
|
4474
|
-
parallel
|
|
4475
|
-
concurrencyLimit:
|
|
4612
|
+
parallel,
|
|
4613
|
+
concurrencyLimit: parallel ? 4 : 1,
|
|
4476
4614
|
});
|
|
4477
4615
|
logger.info('done migration on upgrading', { did, componentDids });
|
|
4478
4616
|
}
|
|
@@ -4530,7 +4668,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
4530
4668
|
|
|
4531
4669
|
await this._runUserHook('preFlight', blocklet, context);
|
|
4532
4670
|
|
|
4533
|
-
await this._runMigration({ needRunDocker, did, blocklet, oldBlocklet, componentDids });
|
|
4671
|
+
await this._runMigration({ parallel: !needRunDocker, did, blocklet, oldBlocklet, componentDids });
|
|
4534
4672
|
|
|
4535
4673
|
// handle component status
|
|
4536
4674
|
const runningDids = [];
|
|
@@ -4696,15 +4834,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
4696
4834
|
|
|
4697
4835
|
return this.blockletDownloader.download(blocklet, {
|
|
4698
4836
|
...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
|
-
},
|
|
4837
|
+
// preDownload: () => {},
|
|
4708
4838
|
onProgress: (data) => {
|
|
4709
4839
|
this.emit(BlockletEvents.downloadBundleProgress, { appDid: appDid || did, meta: { did }, ...data });
|
|
4710
4840
|
},
|
|
@@ -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 };
|