Skip to content

Commit 807f9c7

Browse files
committed
fix(tag): update deploy tag flow
1 parent 313721d commit 807f9c7

File tree

5 files changed

+210
-36
lines changed

5 files changed

+210
-36
lines changed

__tests__/scf.test.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ describe('Scf', () => {
9090
publish: true,
9191
traffic: 0.8,
9292
tags: {
93-
mytest: 'abc',
93+
test: 'test',
9494
},
9595
environment: {
9696
variables: {
@@ -279,7 +279,12 @@ describe('Scf', () => {
279279
CodeResult: 'success',
280280
CodeError: '',
281281
ErrNo: 0,
282-
Tags: expect.any(Array),
282+
Tags: [
283+
{
284+
Key: 'test',
285+
Value: 'test',
286+
},
287+
],
283288
AccessInfo: { Host: '', Vip: '' },
284289
Type: 'Event',
285290
CfsConfig: {

__tests__/tag.test.js

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,52 @@ describe('Tag', () => {
66
SecretKey: process.env.TENCENT_SECRET_KEY,
77
};
88
const functionName = 'serverless-unit-test';
9-
const inputs = {
10-
resource: `qcs::scf:${process.env.REGION}:uin/${process.env.TENCENT_UIN}:namespace/default/function/${functionName}`,
11-
replaceTags: { tagKey: 'tagValue' },
12-
deleteTags: { abcdd: 'def' },
9+
const tagItem = { TagKey: 'slstest', TagValue: 'slstest' };
10+
const commonInputs = {
11+
resourceIds: [`default/function/${functionName}`],
12+
resourcePrefix: 'namespace',
13+
serviceType: 'scf',
1314
};
1415
const tag = new Tag(credentials, process.env.REGION);
1516

16-
test('should success modify tags', async () => {
17-
const res = await tag.deploy(inputs);
18-
const [curTag] = await tag.getScfResourceTags({
17+
test('attach tags', async () => {
18+
delete commonInputs.addTags;
19+
commonInputs.attachTags = [tagItem];
20+
21+
const res = await tag.deploy(commonInputs);
22+
const tagList = await tag.getScfResourceTags({
23+
functionName: functionName,
24+
});
25+
const [exist] = tagList.filter(
26+
(item) => item.TagKey === tagItem.TagKey && item.TagValue === tagItem.TagValue,
27+
);
28+
expect(res).toBe(true);
29+
expect(exist).toBeDefined();
30+
});
31+
32+
test('detach tags', async () => {
33+
delete commonInputs.addTags;
34+
delete commonInputs.attachTags;
35+
commonInputs.detachTags = [tagItem];
36+
37+
const res = await tag.deploy(commonInputs);
38+
const tagList = await tag.getScfResourceTags({
1939
functionName: functionName,
2040
});
41+
const [exist] = tagList.filter(
42+
(item) => item.TagKey === tagItem.TagKey && item.TagValue === tagItem.TagValue,
43+
);
2144
expect(res).toBe(true);
22-
expect(curTag.TagKey).toBe('tagKey');
23-
expect(curTag.TagValue).toBe('tagValue');
45+
expect(exist).toBeUndefined();
46+
});
47+
48+
test('delete tags', async () => {
49+
const res = await tag.deleteTags([tagItem]);
50+
51+
expect(res).toBe(true);
52+
53+
const exist = await tag.isTagExist(tagItem);
54+
55+
expect(exist).toBe(false);
2456
});
2557
});

src/modules/scf/index.js

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -316,17 +316,57 @@ class Scf {
316316
// deploy tags
317317
async deployTags(funcInfo, inputs) {
318318
console.log(`Adding tags for function ${inputs.name} in ${this.region}`);
319-
const deleteTags = {};
320-
for (let i = 0; i < funcInfo.Tags.length; i++) {
321-
if (!inputs.tags.hasOwnProperty(funcInfo.Tags[i].Key)) {
322-
deleteTags[funcInfo.Tags[i].Key] = funcInfo.Tags[i].Value;
319+
const inputKeys = [];
320+
const inputTags = Object.entries(inputs.tags).map(([key, value]) => {
321+
inputKeys.push(key);
322+
return {
323+
Key: key,
324+
Value: value,
325+
};
326+
});
327+
const funcTags = funcInfo.Tags;
328+
const funcTagKeys = funcTags.map((item) => item.Key);
329+
330+
const detachTags = [];
331+
const attachTags = [];
332+
333+
for (let i = 0; i < funcTags.length; i++) {
334+
const funcTag = funcTags[i];
335+
if (inputKeys.indexOf(funcTag.Key) === -1) {
336+
detachTags.push({
337+
TagKey: funcTag.Key,
338+
});
339+
} else {
340+
const inputTagVal = inputs.tags[funcTag.Key];
341+
const funcTagVal = funcTags[i].Value;
342+
if (inputTagVal !== funcTagVal) {
343+
attachTags.push({
344+
TagKey: funcTag.Key,
345+
TagValue: inputTagVal,
346+
});
347+
}
323348
}
324349
}
350+
351+
for (let i = 0; i < inputTags.length; i++) {
352+
const inputTag = inputTags[i];
353+
if (funcTagKeys.indexOf(inputTag.Key) === -1) {
354+
attachTags.push({
355+
TagKey: inputTag.Key,
356+
TagValue: inputTag.Value,
357+
});
358+
}
359+
}
360+
325361
await this.tagClient.deploy({
326-
resource: `qcs::scf:${this.region}::lam/${funcInfo.FunctionId}`,
327-
replaceTags: inputs.tags,
328-
deleteTags: deleteTags,
362+
resourceIds: [`${funcInfo.Namespace}/function/${funcInfo.FunctionName}`],
363+
resourcePrefix: 'namespace',
364+
serviceType: 'scf',
365+
detachTags,
366+
attachTags,
329367
});
368+
369+
return attachTags;
330370
}
331371

332372
// delete function
@@ -636,7 +676,11 @@ class Scf {
636676

637677
// create/update tags
638678
if (inputs.tags) {
639-
await this.deployTags(funcInfo, inputs);
679+
const deployedTags = await this.deployTags(funcInfo, inputs);
680+
outputs.Tags = deployedTags.map((item) => ({
681+
Key: item.TagKey,
682+
Value: item.TagValue,
683+
}));
640684
}
641685

642686
// create/update/delete triggers

src/modules/tag/apis.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
const { ApiFactory } = require('../../utils/api');
22

3-
const ACTIONS = ['ModifyResourceTags', 'DescribeResourceTags'];
3+
const ACTIONS = [
4+
'ModifyResourceTags',
5+
'DescribeResourceTags',
6+
'AttachResourcesTag',
7+
'DetachResourcesTag',
8+
'CreateTag',
9+
'DeleteTag',
10+
'DescribeTags',
11+
];
412

513
const APIS = ApiFactory({
614
// debug: true,

src/modules/tag/index.js

Lines changed: 101 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,27 @@ class Tag {
1919
return result;
2020
}
2121

22+
async getTagList(offset = 0, limit = 100) {
23+
const { Tags, TotalCount } = await this.request({
24+
Action: 'DescribeTags',
25+
Limit: limit,
26+
Offset: offset,
27+
});
28+
if (TotalCount > limit) {
29+
return Tags.concat(await this.getTagList(offset + limit, limit));
30+
}
31+
32+
return Tags;
33+
}
34+
35+
async isTagExist(tag) {
36+
const tagList = await this.getTagList();
37+
const [exist] = tagList.filter(
38+
(item) => item.TagKey === tag.TagKey && item.TagValue === tag.TagValue,
39+
);
40+
return !!exist;
41+
}
42+
2243
async getScfResourceTags(inputs) {
2344
const data = {
2445
Action: 'DescribeResourceTags',
@@ -30,33 +51,97 @@ class Tag {
3051
return Rows;
3152
}
3253

33-
async deploy(inputs = {}) {
34-
const tagsInputs = {
35-
Action: 'ModifyResourceTags',
36-
Resource: inputs.resource,
54+
async attachTags({ serviceType, resourcePrefix, resourceIds, tags }) {
55+
const commonInputs = {
56+
Action: 'AttachResourcesTag',
57+
ResourceIds: resourceIds,
58+
ServiceType: serviceType,
59+
ResourceRegion: this.region,
60+
ResourcePrefix: resourcePrefix,
3761
};
62+
// if tag not exsit, create it
3863

39-
const { replaceTags = {}, deleteTags = {} } = inputs;
64+
for (let i = 0; i < tags.length; i++) {
65+
const currentTag = tags[i];
66+
const tagExist = await this.isTagExist(currentTag);
67+
if (!tagExist) {
68+
await this.createTag(currentTag);
69+
}
70+
const tagInputs = {
71+
...commonInputs,
72+
...currentTag,
73+
};
74+
await this.request(tagInputs);
75+
}
76+
}
4077

41-
if (Object.keys(replaceTags).length > 0) {
42-
tagsInputs.ReplaceTags = Object.entries(replaceTags).map(([key, val]) => ({
43-
TagKey: key,
44-
TagValue: val,
45-
}));
78+
async detachTags({ serviceType, resourcePrefix, resourceIds, tags }) {
79+
const commonInputs = {
80+
Action: 'DetachResourcesTag',
81+
ResourceIds: resourceIds,
82+
ServiceType: serviceType,
83+
ResourceRegion: this.region,
84+
ResourcePrefix: resourcePrefix,
85+
};
86+
for (let i = 0; i < tags.length; i++) {
87+
const tagInputs = {
88+
...commonInputs,
89+
...tags[i],
90+
};
91+
delete tagInputs.TagValue;
92+
await this.request(tagInputs);
4693
}
47-
if (Object.keys(deleteTags).length > 0) {
48-
tagsInputs.DeleteTags = Object.keys(deleteTags).map((key) => ({
49-
TagKey: key,
50-
}));
94+
}
95+
96+
async createTag(tag) {
97+
console.log(`Creating tag key: ${tag.TagKey}, value: ${tag.TagValue}`);
98+
await this.request({
99+
Action: 'CreateTag',
100+
...tag,
101+
});
102+
103+
return tag;
104+
}
105+
106+
async deleteTag(tag) {
107+
console.log(`Deleting tag key: ${tag.TagKey}, value: ${tag.TagValue}`);
108+
await this.request({
109+
Action: 'DeleteTag',
110+
...tag,
111+
});
112+
113+
return true;
114+
}
115+
116+
async deleteTags(tags) {
117+
for (let i = 0; i < tags.length; i++) {
118+
await this.deleteTag(tags[i]);
51119
}
52120

121+
return true;
122+
}
123+
124+
async deploy(inputs = {}) {
125+
const { detachTags = [], attachTags = [], serviceType, resourceIds, resourcePrefix } = inputs;
126+
53127
console.log(`Updating tags`);
54128
try {
55-
await this.request(tagsInputs);
129+
await this.detachTags({
130+
tags: detachTags,
131+
serviceType,
132+
resourceIds,
133+
resourcePrefix,
134+
});
135+
await this.attachTags({
136+
tags: attachTags,
137+
serviceType,
138+
resourceIds,
139+
resourcePrefix,
140+
});
56141
} catch (e) {
57142
console.log(e);
58143
}
59-
console.log(`Update tags success.`);
144+
console.log(`Update tags success`);
60145

61146
return true;
62147
}

0 commit comments

Comments
 (0)