Skip to content

Commit e153d64

Browse files
committed
fix(scf): optimize deploy flow
1 parent 7c93f18 commit e153d64

File tree

5 files changed

+99
-24
lines changed

5 files changed

+99
-24
lines changed

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
},
2020
"husky": {
2121
"hooks": {
22-
"pre-commit": "lint-staged",
22+
"pre-commit": "ygsec && lint-staged",
2323
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
24-
"pre-push": "npm run lint:fix && npm run prettier:fix"
24+
"pre-push": "ygsec && npm run lint:fix && npm run prettier:fix"
2525
}
2626
},
2727
"lint-staged": {
@@ -72,6 +72,7 @@
7272
"dependencies": {
7373
"@tencent-sdk/capi": "^0.3.1",
7474
"@ygkit/request": "^0.1.1",
75+
"@ygkit/secure": "^0.0.3",
7576
"cos-nodejs-sdk-v5": "^2.6.2",
7677
"moment": "^2.25.3",
7778
"tencent-cloud-sdk": "^1.0.3"

src/modules/layer/index.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const { Capi } = require('@tencent-sdk/capi');
2+
const capis = require('./apis/apis');
23
const apis = require('./apis');
34

45
class Layer {
@@ -12,9 +13,19 @@ class Layer {
1213
Token: credentials.Token,
1314
});
1415
}
15-
async checkExist(name) {
16-
const res = await apis.getLayerDetail(this.capi, name);
17-
return !!res.LayerVersion;
16+
17+
async request({ Action, ...data }) {
18+
const result = await capis[Action](this.capi, data);
19+
return result;
20+
}
21+
22+
async getLayerDetail(name, version) {
23+
try {
24+
const detail = await apis.getLayerDetail(this.capi, name, version);
25+
return detail;
26+
} catch (e) {
27+
return null;
28+
}
1829
}
1930

2031
async deploy(inputs = {}) {

src/modules/layer/index.test.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,21 @@ async function runTest() {
1616
runtimes: ['Nodejs10.15', 'Nodejs12.16'],
1717
};
1818
const layer = new Layer(credentials, inputs.region);
19-
const outputs = await layer.deploy(inputs);
20-
console.log('outputs', JSON.stringify(outputs));
19+
const res1 = await layer.deploy(inputs);
20+
console.log('deploy result: ', res1);
21+
console.log('+++++++++++++++++++++');
22+
23+
// get layer
24+
const res2 = await layer.getLayerDetail(inputs.name, res1.version);
25+
console.log('get detail: ', res2);
26+
console.log('+++++++++++++++++++++');
2127

2228
await sleep(1000);
23-
await layer.remove(outputs);
29+
await layer.remove({
30+
name: inputs.name,
31+
version: res1.version,
32+
});
33+
2434
}
2535

2636
runTest();

src/modules/scf/config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ const CONFIGS = {
33
defaultMemorySize: 128,
44
defaultTimeout: 3,
55
defaultInitTimeout: 3,
6-
waitStatus: ['Creating', 'Updating', 'Publishing'],
6+
waitStatus: ['Creating', 'Updating', 'Publishing', 'Deleting'],
7+
failStatus: ['CreateFailed ', 'UpdateFailed', 'PublishFailed', 'DeleteFailed'],
78
};
89

910
module.exports = CONFIGS;

src/modules/scf/index.js

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,12 @@ class Scf {
3131
return result;
3232
}
3333

34-
// 绑定默认策略
34+
// bind SCF_QcsRole role
3535
async bindScfQCSRole() {
3636
console.log(`Creating and binding SCF_QcsRole`);
3737
const camClient = new Cam(this.credentials);
3838
const roleName = 'SCF_QcsRole';
3939
const policyId = 28341895;
40-
// 创建默认角色
4140
try {
4241
await camClient.request({
4342
Action: 'CreateRole',
@@ -58,7 +57,6 @@ class Scf {
5857
}),
5958
});
6059
} catch (e) {}
61-
// 绑定默认策略
6260
try {
6361
await camClient.request({
6462
Action: 'AttachRolePolicy',
@@ -99,21 +97,27 @@ class Scf {
9997
// because creating/upadting function is asynchronous
10098
// if not become Active in 120 * 1000 miniseconds, return request result, and throw error
10199
async checkStatus(namespace = 'default', functionName, qualifier = '$LATEST') {
102-
console.log(`Checking function ${functionName} status`);
103100
let initialInfo = await this.getFunction(namespace, functionName, qualifier);
104-
let status = initialInfo;
101+
let { Status } = initialInfo;
105102
let times = 120;
106-
while (CONFIGS.waitStatus.indexOf(status) !== -1 && times > 0) {
103+
while (CONFIGS.waitStatus.indexOf(Status) !== -1 && times > 0) {
107104
initialInfo = await this.getFunction(namespace, functionName, qualifier);
108-
status = initialInfo.Status;
105+
if (!initialInfo) {
106+
return true;
107+
}
108+
({ Status } = initialInfo);
109+
// if change to failed status break loop
110+
if (CONFIGS.failStatus.indexOf(Status) !== -1) {
111+
break;
112+
}
109113
await sleep(1000);
110114
times = times - 1;
111115
}
112116
const { StatusReasons } = initialInfo;
113-
return status !== 'Active'
117+
return Status !== 'Active'
114118
? StatusReasons && StatusReasons.length > 0
115119
? `函数状态异常, ${StatusReasons[0].ErrorMessage}`
116-
: `函数状态异常, ${status}`
120+
: `函数状态异常, ${Status}`
117121
: true;
118122
}
119123

@@ -237,7 +241,7 @@ class Scf {
237241
}
238242

239243
// 删除函数
240-
async deleteFunction(functionName, namespace) {
244+
async deleteFunction(namespace, functionName) {
241245
await this.request({
242246
Action: 'DeleteFunction',
243247
FunctionName: functionName,
@@ -315,7 +319,7 @@ class Scf {
315319
}
316320

317321
/**
318-
* check whether function status is operational
322+
* check whether function status is operational, mostly for asynchronous operation
319323
* @param {string} namespace
320324
* @param {string} functionName funcitn name
321325
*/
@@ -328,15 +332,63 @@ class Scf {
328332
throw new TypeError('API_SCF_isOperationalStatus', res);
329333
}
330334

335+
async tryToDeleteFunction(namespace, functionName) {
336+
try {
337+
console.log(`正在尝试删除创建失败的函数,命令空间:${namespace},函数名称:${functionName}`);
338+
await this.deleteFunction(namespace, functionName);
339+
await this.isOperationalStatus(namespace, functionName);
340+
} catch (e) {}
341+
}
342+
343+
// check whether scf is operational
344+
async isOperational(namespace, functionName, qualifier = '$LATEST') {
345+
const funcInfo = await this.getFunction(namespace, functionName, qualifier);
346+
if (funcInfo) {
347+
const { Status, StatusReasons } = funcInfo;
348+
const reason = StatusReasons && StatusReasons.length > 0 ? StatusReasons[0].ErrorMessage : '';
349+
if (Status === 'Active') {
350+
return true;
351+
}
352+
let errorMsg = '';
353+
switch (Status) {
354+
case 'Creating':
355+
errorMsg = '当前函数正在创建中,无法更新代码,请稍后再试';
356+
break;
357+
case 'Updating':
358+
errorMsg = '当前函数正在更新中,无法更新代码,请稍后再试';
359+
break;
360+
case 'Publishing':
361+
errorMsg = '当前函数正在版本发布中,无法更新代码,请稍后再试';
362+
break;
363+
case 'Deleting':
364+
errorMsg = '当前函数正在删除中,无法更新代码,请稍后再试';
365+
break;
366+
case 'CreateFailed':
367+
console.log(`函数创建失败,${reason || Status}`);
368+
await this.tryToDeleteFunction(namespace, functionName);
369+
break;
370+
case 'DeleteFailed':
371+
errorMsg = `函数删除失败,${reason || Status}`;
372+
break;
373+
}
374+
if (errorMsg) {
375+
throw new TypeError('API_SCF_isOperational', errorMsg);
376+
}
377+
}
378+
}
379+
331380
// deploy SCF flow
332381
async deploy(inputs = {}) {
382+
const namespace = inputs.namespace || CONFIGS.defaultNamespace;
383+
384+
// before deploy a scf, we should check whether
385+
// if is CreateFailed, try to remove it
386+
await this.isOperational(namespace, inputs.name);
387+
333388
// whether auto create/bind role
334389
if (inputs.enableRoleAuth) {
335390
await this.bindScfQCSRole();
336391
}
337-
338-
const namespace = inputs.namespace || CONFIGS.defaultNamespace;
339-
340392
// check SCF exist
341393
// exist: update it, not: create it
342394
let funcInfo = await this.getFunction(namespace, inputs.name);
@@ -452,7 +504,7 @@ class Scf {
452504
return;
453505
}
454506

455-
await this.deleteFunction(functionName, namespace);
507+
await this.deleteFunction(namespace, functionName);
456508

457509
if (inputs.Triggers) {
458510
for (let i = 0; i < inputs.Triggers.length; i++) {

0 commit comments

Comments
 (0)