@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
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/* eslint-disable no-await-in-loop */
|
|
2
|
+
const logger = require('@abtnode/logger')('@abtnode/core:blocklet-manager:blue-green');
|
|
3
|
+
const { BlockletStatus, BlockletGroup, BlockletEvents } = require('@blocklet/constant');
|
|
4
|
+
const {
|
|
5
|
+
hasRunnableComponent,
|
|
6
|
+
getDisplayName,
|
|
7
|
+
isExternalBlocklet,
|
|
8
|
+
forEachBlockletSync,
|
|
9
|
+
getComponentMissingConfigs,
|
|
10
|
+
} = require('@blocklet/meta/lib/util');
|
|
11
|
+
const { getBlockletEngine, hasStartEngine } = require('@blocklet/meta/lib/engine');
|
|
12
|
+
const {
|
|
13
|
+
forEachBlocklet,
|
|
14
|
+
validateBlocklet,
|
|
15
|
+
validateBlockletChainInfo,
|
|
16
|
+
ensureAppPortsNotOccupied,
|
|
17
|
+
getHealthyCheckTimeout,
|
|
18
|
+
getHookArgs,
|
|
19
|
+
shouldSkipComponent,
|
|
20
|
+
} = require('../../../util/blocklet');
|
|
21
|
+
const { startBlockletProcess } = require('../../../util/blocklet');
|
|
22
|
+
const hooks = require('../../hooks');
|
|
23
|
+
const checkNeedRunDocker = require('../../../util/docker/check-need-run-docker');
|
|
24
|
+
const { blueGreenGetComponentIds } = require('./blue-green-get-componentids');
|
|
25
|
+
const { dockerExec } = require('../../../util/docker/docker-exec');
|
|
26
|
+
const { isDockerOnlySingleInstance } = require('../../../util/docker/is-docker-only-single-instances');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 绿(Green)环境:指一个并行的、与蓝环境几乎一模一样的环境,用来部署新的版本。新代码、新配置都会先部署到绿环境中。
|
|
30
|
+
*
|
|
31
|
+
* @param {Object} params - 启动参数
|
|
32
|
+
* @param {string} params.did - blocklet DID
|
|
33
|
+
* @param {Array<string>} params.componentDids - 组件 DID 列表
|
|
34
|
+
* @param {string} params.operator - 操作者
|
|
35
|
+
* @param {Object} context - 上下文信息
|
|
36
|
+
* @param {Object} manager - blocklet 管理器实例
|
|
37
|
+
* @param {Object} states - 状态管理器
|
|
38
|
+
* @returns {Promise<Object>} 返回启动后的 blocklet 对象
|
|
39
|
+
*/
|
|
40
|
+
const blueGreenStartBlocklet = async (
|
|
41
|
+
{ did, componentDids, operator: _operator, ignoreErrorNotification },
|
|
42
|
+
context,
|
|
43
|
+
manager,
|
|
44
|
+
states
|
|
45
|
+
) => {
|
|
46
|
+
const operator = _operator || context?.user?.did;
|
|
47
|
+
const throwOnError = true;
|
|
48
|
+
const checkHealthImmediately = true;
|
|
49
|
+
const e2eMode = false;
|
|
50
|
+
|
|
51
|
+
logger.info('start green blocklet (blue-green deployment)', {
|
|
52
|
+
did,
|
|
53
|
+
componentDids,
|
|
54
|
+
throwOnError,
|
|
55
|
+
checkHealthImmediately,
|
|
56
|
+
e2eMode,
|
|
57
|
+
operator,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// 获取并验证 blocklet
|
|
61
|
+
const blocklet1 = await manager.ensureBlocklet(did, { e2eMode });
|
|
62
|
+
did = blocklet1.meta.did; // eslint-disable-line no-param-reassign
|
|
63
|
+
|
|
64
|
+
// 验证组件需求和引擎
|
|
65
|
+
await validateBlocklet(blocklet1);
|
|
66
|
+
await validateBlockletChainInfo(blocklet1);
|
|
67
|
+
|
|
68
|
+
if (!hasRunnableComponent(blocklet1)) {
|
|
69
|
+
throw new Error('No runnable component found');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const customerDockerUseVolumeComponentIds = [];
|
|
73
|
+
const otherComponentIds = [];
|
|
74
|
+
await forEachBlockletSync(blocklet1, (b) => {
|
|
75
|
+
if (!componentDids.includes(b.meta.did)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (isDockerOnlySingleInstance(b.meta)) {
|
|
79
|
+
customerDockerUseVolumeComponentIds.push(b.meta.did);
|
|
80
|
+
} else {
|
|
81
|
+
otherComponentIds.push(b.meta.did);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (customerDockerUseVolumeComponentIds.length) {
|
|
86
|
+
await manager.stop(
|
|
87
|
+
{ did, componentDids: customerDockerUseVolumeComponentIds, updateStatus: false, operator },
|
|
88
|
+
context
|
|
89
|
+
);
|
|
90
|
+
await manager.start({ did, componentDids: customerDockerUseVolumeComponentIds, operator }, context);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 分类组件 ID
|
|
94
|
+
const entryComponentIds = [];
|
|
95
|
+
const nonEntryComponentIds = [];
|
|
96
|
+
const componentDidsSet = new Set(otherComponentIds);
|
|
97
|
+
|
|
98
|
+
await forEachBlocklet(
|
|
99
|
+
blocklet1,
|
|
100
|
+
(b) => {
|
|
101
|
+
if (!componentDidsSet.has(b.meta.did)) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (b.meta.group === BlockletGroup.gateway) {
|
|
106
|
+
nonEntryComponentIds.push(b.meta.did);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const engine = getBlockletEngine(b.meta);
|
|
110
|
+
if (engine.interpreter === 'blocklet') {
|
|
111
|
+
nonEntryComponentIds.push(b.meta.did);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (!hasStartEngine(b.meta)) {
|
|
115
|
+
nonEntryComponentIds.push(b.meta.did);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
entryComponentIds.push(b.meta.did);
|
|
119
|
+
},
|
|
120
|
+
{ parallel: true, concurrencyLimit: 4 }
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
if (nonEntryComponentIds.length) {
|
|
124
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
|
|
125
|
+
componentDids: nonEntryComponentIds,
|
|
126
|
+
operator,
|
|
127
|
+
isGreen: true,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!entryComponentIds.length) {
|
|
132
|
+
await manager.emit(BlockletEvents.started, {
|
|
133
|
+
...blocklet1,
|
|
134
|
+
componentDids: nonEntryComponentIds,
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// check required config
|
|
140
|
+
try {
|
|
141
|
+
for (const component of blocklet1.children) {
|
|
142
|
+
if (!entryComponentIds.includes(component.meta.did)) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (!shouldSkipComponent(component.meta.did, entryComponentIds)) {
|
|
146
|
+
const missingProps = getComponentMissingConfigs(component, blocklet1);
|
|
147
|
+
if (missingProps.length) {
|
|
148
|
+
throw new Error(
|
|
149
|
+
`Missing required configuration to start ${component.meta.title}: ${missingProps.map((x) => x.key).join(',')}`
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} catch (error) {
|
|
155
|
+
const description = `Green environment start failed for ${getDisplayName(blocklet1)}: ${error.message}`;
|
|
156
|
+
if (!ignoreErrorNotification) {
|
|
157
|
+
manager._createNotification(did, {
|
|
158
|
+
title: 'Blue-Green Deployment: Green Start Failed',
|
|
159
|
+
description,
|
|
160
|
+
entityType: 'blocklet',
|
|
161
|
+
entityId: did,
|
|
162
|
+
severity: 'error',
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const blueGreenComponentIds = await blueGreenGetComponentIds(blocklet1, entryComponentIds);
|
|
169
|
+
|
|
170
|
+
// eslint-disable-next-line no-unreachable-loop
|
|
171
|
+
await Promise.all(
|
|
172
|
+
blueGreenComponentIds.map(async (item) => {
|
|
173
|
+
if (!item.componentDids.length) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const nextBlocklet = await ensureAppPortsNotOccupied({
|
|
177
|
+
blocklet: blocklet1,
|
|
178
|
+
componentDids: item.componentDids,
|
|
179
|
+
states,
|
|
180
|
+
manager,
|
|
181
|
+
isGreen: item.changeToGreen,
|
|
182
|
+
});
|
|
183
|
+
try {
|
|
184
|
+
const doc1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.starting, {
|
|
185
|
+
componentDids: item.componentDids,
|
|
186
|
+
operator,
|
|
187
|
+
isGreen: item.changeToGreen,
|
|
188
|
+
});
|
|
189
|
+
nextBlocklet.greenStatus = BlockletStatus.starting;
|
|
190
|
+
manager.emit(BlockletEvents.statusChange, doc1);
|
|
191
|
+
|
|
192
|
+
const nodeInfo = await states.node.read();
|
|
193
|
+
const nodeEnvironments = await states.node.getEnvironments();
|
|
194
|
+
|
|
195
|
+
// 钩子函数设置
|
|
196
|
+
const getHookFn =
|
|
197
|
+
(hookName) =>
|
|
198
|
+
async (b, { env }) => {
|
|
199
|
+
const hookArgs = getHookArgs(b);
|
|
200
|
+
const needRunDocker = await checkNeedRunDocker(b.meta, env, nodeInfo, isExternalBlocklet(nextBlocklet));
|
|
201
|
+
if (!b.meta.scripts?.[hookName]) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
if (needRunDocker) {
|
|
205
|
+
return dockerExec({
|
|
206
|
+
blocklet: nextBlocklet,
|
|
207
|
+
meta: b.meta,
|
|
208
|
+
script: b.meta.scripts?.[hookName],
|
|
209
|
+
hookName,
|
|
210
|
+
nodeInfo,
|
|
211
|
+
env,
|
|
212
|
+
...hookArgs,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return hooks[hookName](b, {
|
|
216
|
+
appDir: b.env.appDir,
|
|
217
|
+
hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
|
|
218
|
+
env,
|
|
219
|
+
did, // root blocklet did,
|
|
220
|
+
teamManager: manager.teamManager,
|
|
221
|
+
...hookArgs,
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
await startBlockletProcess(nextBlocklet, {
|
|
226
|
+
...context,
|
|
227
|
+
preFlight: getHookFn('preFlight'),
|
|
228
|
+
preStart: getHookFn('preStart'),
|
|
229
|
+
postStart: getHookFn('postStart'),
|
|
230
|
+
nodeEnvironments,
|
|
231
|
+
nodeInfo,
|
|
232
|
+
e2eMode,
|
|
233
|
+
componentDids: item.componentDids,
|
|
234
|
+
configSynchronizer: manager.configSynchronizer,
|
|
235
|
+
isGreen: item.changeToGreen,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// 健康检查绿色环境
|
|
239
|
+
const { startTimeout, minConsecutiveTime } = getHealthyCheckTimeout(nextBlocklet, {
|
|
240
|
+
checkHealthImmediately,
|
|
241
|
+
componentDids: item.componentDids,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
await manager._onCheckIfStarted(
|
|
245
|
+
{
|
|
246
|
+
did,
|
|
247
|
+
context,
|
|
248
|
+
minConsecutiveTime,
|
|
249
|
+
timeout: startTimeout,
|
|
250
|
+
componentDids: item.componentDids,
|
|
251
|
+
},
|
|
252
|
+
{ throwOnError: true, isGreen: item.changeToGreen, needUpdateBlueStatus: true }
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
logger.info('Green environment started successfully', {
|
|
256
|
+
did,
|
|
257
|
+
componentDids: item.componentDids,
|
|
258
|
+
});
|
|
259
|
+
} catch (err) {
|
|
260
|
+
const error = Array.isArray(err) ? err[0] : err;
|
|
261
|
+
logger.error('Failed to start green environment', { error, did, title: blocklet1.meta.title });
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
await manager.deleteProcess({ did, componentDids: item.componentDids, isGreen: item.changeToGreen });
|
|
265
|
+
manager.emit(BlockletEvents.statusChange, blocklet1);
|
|
266
|
+
} catch (cleanupError) {
|
|
267
|
+
logger.error('Failed to cleanup green environment', { cleanupError });
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const description = `Green environment start failed for ${getDisplayName(blocklet1)}: ${error.message}`;
|
|
271
|
+
if (!ignoreErrorNotification) {
|
|
272
|
+
manager._createNotification(did, {
|
|
273
|
+
title: 'Blue-Green Deployment: Green Start Failed',
|
|
274
|
+
description,
|
|
275
|
+
entityType: 'blocklet',
|
|
276
|
+
entityId: did,
|
|
277
|
+
severity: 'error',
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (throwOnError) {
|
|
282
|
+
throw new Error(description);
|
|
283
|
+
}
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
})
|
|
287
|
+
);
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
module.exports = {
|
|
291
|
+
blueGreenStartBlocklet,
|
|
292
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const blueGreenUpdateBlockletStatus = async ({ states, did, status, blueGreenComponentIds }) => {
|
|
2
|
+
const outputBlocklet = {};
|
|
3
|
+
await Promise.all(
|
|
4
|
+
blueGreenComponentIds.map(async (item) => {
|
|
5
|
+
if (!item.componentDids.length) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const res = await states.blocklet.setBlockletStatus(did, status, {
|
|
9
|
+
componentDids: item.componentDids,
|
|
10
|
+
isGreen: item.changeToGreen,
|
|
11
|
+
});
|
|
12
|
+
Object.assign(outputBlocklet, res);
|
|
13
|
+
})
|
|
14
|
+
);
|
|
15
|
+
return outputBlocklet;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
module.exports = { blueGreenUpdateBlockletStatus };
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/* eslint-disable no-await-in-loop */
|
|
2
|
+
const logger = require('@abtnode/logger')('@abtnode/core:blocklet-manager:upgrade-blue-green');
|
|
3
|
+
const { BlockletStatus, BlockletEvents, BlockletInternalEvents } = require('@blocklet/constant');
|
|
4
|
+
const { INSTALL_ACTIONS, WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
5
|
+
const { getDisplayName, hasStartEngine } = require('@blocklet/meta/lib/util');
|
|
6
|
+
const { getComponentsInternalInfo } = require('@blocklet/meta/lib/blocklet');
|
|
7
|
+
const { getComponentNamesWithVersion, updateBlockletFallbackLogo } = require('../../../util/blocklet');
|
|
8
|
+
const { blueGreenStartBlocklet } = require('./blue-green-start-blocklet');
|
|
9
|
+
|
|
10
|
+
const blueGreenUpgradeBlocklet = async (
|
|
11
|
+
{ newBlocklet, oldBlocklet, componentDids, action, shouldCleanUploadFile, url },
|
|
12
|
+
context,
|
|
13
|
+
manager,
|
|
14
|
+
states
|
|
15
|
+
) => {
|
|
16
|
+
const { meta, source, deployedFrom, children } = newBlocklet;
|
|
17
|
+
const { did, version, name } = meta;
|
|
18
|
+
const title = getDisplayName(newBlocklet);
|
|
19
|
+
|
|
20
|
+
for (const child of newBlocklet.children) {
|
|
21
|
+
for (const child2 of oldBlocklet.children) {
|
|
22
|
+
if (child && child2 && child.meta.did === child2.meta.did) {
|
|
23
|
+
child.status = child2.status;
|
|
24
|
+
child.greenStatus = child2.greenStatus;
|
|
25
|
+
child.ports = child2.ports;
|
|
26
|
+
child.greenPorts = child2.greenPorts;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await states.blocklet.upgradeBlocklet({ meta, source, deployedFrom, children });
|
|
33
|
+
logger.info('updated blocklet for upgrading', { did, componentDids, source, name });
|
|
34
|
+
|
|
35
|
+
await manager._setConfigsFromMeta(did);
|
|
36
|
+
|
|
37
|
+
let blocklet = await manager.ensureBlocklet(did);
|
|
38
|
+
|
|
39
|
+
await manager._updateBlockletEnvironment(did);
|
|
40
|
+
blocklet = await manager.getBlocklet(did);
|
|
41
|
+
|
|
42
|
+
await manager._runUserHook('preInstall', blocklet, context);
|
|
43
|
+
await manager._runUserHook('postInstall', blocklet, context);
|
|
44
|
+
await manager._runUserHook('preFlight', blocklet, context);
|
|
45
|
+
await manager._runMigration({
|
|
46
|
+
parallel: false,
|
|
47
|
+
did,
|
|
48
|
+
blocklet,
|
|
49
|
+
oldBlocklet,
|
|
50
|
+
componentDids,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const runningDids = [];
|
|
54
|
+
const stoppedDids = [];
|
|
55
|
+
|
|
56
|
+
if (action === INSTALL_ACTIONS.INSTALL_COMPONENT) {
|
|
57
|
+
for (const componentDid of componentDids) {
|
|
58
|
+
const component = blocklet.children.find((x) => x.meta.did === componentDid);
|
|
59
|
+
if (!component) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (hasStartEngine(component.meta)) {
|
|
63
|
+
stoppedDids.push(componentDid);
|
|
64
|
+
} else {
|
|
65
|
+
runningDids.push(componentDid);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
for (const componentDid of componentDids) {
|
|
70
|
+
const oldComponent = oldBlocklet.children.find((x) => x.meta.did === componentDid);
|
|
71
|
+
if (oldComponent?.status === BlockletStatus.running || oldComponent?.greenStatus === BlockletStatus.running) {
|
|
72
|
+
runningDids.push(componentDid);
|
|
73
|
+
} else {
|
|
74
|
+
stoppedDids.push(componentDid);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const initialized = !!blocklet.settings?.initialized;
|
|
80
|
+
|
|
81
|
+
if (runningDids.length) {
|
|
82
|
+
if (initialized) {
|
|
83
|
+
await blueGreenStartBlocklet(
|
|
84
|
+
{ did, componentDids: runningDids, operator: context?.user?.did, ignoreErrorNotification: true },
|
|
85
|
+
context,
|
|
86
|
+
manager,
|
|
87
|
+
states
|
|
88
|
+
);
|
|
89
|
+
await states.blocklet.setInstalledAt(did);
|
|
90
|
+
} else {
|
|
91
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, {
|
|
92
|
+
componentDids: runningDids,
|
|
93
|
+
isGreenAndBlue: true,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (stoppedDids.length) {
|
|
99
|
+
const status = action === INSTALL_ACTIONS.INSTALL_COMPONENT ? BlockletStatus.installed : BlockletStatus.stopped;
|
|
100
|
+
await states.blocklet.setBlockletStatus(did, status, {
|
|
101
|
+
componentDids: stoppedDids,
|
|
102
|
+
isGreenAndBlue: true,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
blocklet = await manager.getBlocklet(did, context);
|
|
107
|
+
|
|
108
|
+
await updateBlockletFallbackLogo(blocklet);
|
|
109
|
+
|
|
110
|
+
await manager._updateDependents(did);
|
|
111
|
+
|
|
112
|
+
manager.refreshListCache();
|
|
113
|
+
|
|
114
|
+
manager.configSynchronizer.throttledSyncAppConfig(blocklet);
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
manager.emit(BlockletEvents.upgraded, { blocklet, context });
|
|
118
|
+
|
|
119
|
+
const isInstallAction = action === INSTALL_ACTIONS.INSTALL_COMPONENT;
|
|
120
|
+
const notificationEvent = isInstallAction ? BlockletEvents.componentInstalled : BlockletEvents.componentUpgraded;
|
|
121
|
+
const actionName = isInstallAction ? 'installed' : 'upgraded';
|
|
122
|
+
|
|
123
|
+
manager.emit(notificationEvent, { ...blocklet, componentDids, oldBlocklet, context });
|
|
124
|
+
|
|
125
|
+
manager._createNotification(did, {
|
|
126
|
+
title: `Component ${actionName} succeed`,
|
|
127
|
+
description: `${getComponentNamesWithVersion(
|
|
128
|
+
newBlocklet,
|
|
129
|
+
componentDids
|
|
130
|
+
)} is ${actionName} successfully for ${title}`,
|
|
131
|
+
action: `/blocklets/${did}/overview`,
|
|
132
|
+
blockletDashboardAction: `${WELLKNOWN_SERVICE_PATH_PREFIX}/admin/blocklets`,
|
|
133
|
+
entityType: 'blocklet',
|
|
134
|
+
entityId: did,
|
|
135
|
+
severity: 'success',
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (shouldCleanUploadFile && url) {
|
|
139
|
+
manager._cleanUploadFile(url);
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
logger.error('emit upgrade notification failed', { name, version, error });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
await manager._ensureDeletedChildrenInSettings(blocklet);
|
|
146
|
+
|
|
147
|
+
if (oldBlocklet.status === BlockletStatus.running || oldBlocklet.greenStatus === BlockletStatus.running) {
|
|
148
|
+
manager.emit(
|
|
149
|
+
action === INSTALL_ACTIONS.INSTALL_COMPONENT
|
|
150
|
+
? BlockletInternalEvents.componentInstalled
|
|
151
|
+
: BlockletInternalEvents.componentUpgraded,
|
|
152
|
+
{
|
|
153
|
+
appDid: blocklet.appDid,
|
|
154
|
+
components: getComponentsInternalInfo(blocklet).filter((c) => componentDids.includes(c.did)),
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return blocklet;
|
|
160
|
+
} catch (err) {
|
|
161
|
+
logger.error('failed to upgrade blocklet', { did, version, name, error: err });
|
|
162
|
+
manager.configSynchronizer.throttledSyncAppConfig(oldBlocklet);
|
|
163
|
+
await manager._updateDependents(did);
|
|
164
|
+
|
|
165
|
+
const actionName = action === INSTALL_ACTIONS.INSTALL_COMPONENT ? 'install' : 'upgrade';
|
|
166
|
+
const notificationEvent =
|
|
167
|
+
action === INSTALL_ACTIONS.INSTALL_COMPONENT
|
|
168
|
+
? BlockletEvents.componentInstallFailed
|
|
169
|
+
: BlockletEvents.componentUpgradeFailed;
|
|
170
|
+
|
|
171
|
+
manager.emit(notificationEvent, {
|
|
172
|
+
blocklet: { ...newBlocklet, componentDids, error: { message: err.message } },
|
|
173
|
+
context,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
manager._createNotification(did, {
|
|
177
|
+
title: `Component ${actionName} failed`,
|
|
178
|
+
description: `${getComponentNamesWithVersion(newBlocklet, componentDids)} ${actionName} failed for ${title}: ${
|
|
179
|
+
err.message
|
|
180
|
+
}.`,
|
|
181
|
+
entityType: 'blocklet',
|
|
182
|
+
entityId: did,
|
|
183
|
+
severity: 'error',
|
|
184
|
+
});
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
module.exports = {
|
|
190
|
+
blueGreenUpgradeBlocklet,
|
|
191
|
+
};
|
|
@@ -3,7 +3,7 @@ const cloneDeep = require('@abtnode/util/lib/deep-clone');
|
|
|
3
3
|
|
|
4
4
|
const logger = require('@abtnode/logger')('@abtnode/core:upgrade-component');
|
|
5
5
|
|
|
6
|
-
const {
|
|
6
|
+
const { BlockletEvents } = require('@blocklet/constant');
|
|
7
7
|
const { INSTALL_ACTIONS } = require('@abtnode/constant');
|
|
8
8
|
const { parseOptionalComponents } = require('@blocklet/resolver');
|
|
9
9
|
const {
|
|
@@ -127,17 +127,11 @@ const upgrade = async ({ updateId, componentDids, context, states, manager }) =>
|
|
|
127
127
|
children: children.map((x) => ({ name: x.meta.name, version: x.meta.version })),
|
|
128
128
|
});
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
const newBlocklet = await states.blocklet.setBlockletStatus(did, BlockletStatus.waiting, { componentDids });
|
|
132
|
-
|
|
133
|
-
newBlocklet.children = children;
|
|
130
|
+
const newBlocklet = { ...oldBlocklet, children };
|
|
134
131
|
await validateBlocklet(newBlocklet);
|
|
135
132
|
|
|
136
|
-
manager.emit(BlockletEvents.statusChange, newBlocklet);
|
|
137
|
-
|
|
138
133
|
const action = INSTALL_ACTIONS.UPGRADE_COMPONENT;
|
|
139
134
|
|
|
140
|
-
// backup rollback data
|
|
141
135
|
await manager._rollbackCache.backup({ did, action, oldBlocklet });
|
|
142
136
|
|
|
143
137
|
// add to queue
|
|
@@ -158,7 +152,6 @@ const upgrade = async ({ updateId, componentDids, context, states, manager }) =>
|
|
|
158
152
|
ticket.on('failed', async (err) => {
|
|
159
153
|
logger.error('queue failed', { entity: 'blocklet', action, did, error: err });
|
|
160
154
|
await manager._rollback(action, did, oldBlocklet);
|
|
161
|
-
|
|
162
155
|
manager.emit(BlockletEvents.componentUpgradeFailed, {
|
|
163
156
|
blocklet: { ...oldBlocklet, componentDids, error: { message: err.message } },
|
|
164
157
|
context: { ...context, createAuditLog: false },
|