Skip to content

Commit 3dd0c8f

Browse files
committed
feat: support multi region and cls config
1 parent 5e8a3f2 commit 3dd0c8f

File tree

8 files changed

+242
-303
lines changed

8 files changed

+242
-303
lines changed

docs/configure.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ inputs:
3333
functionConf: # 函数配置相关
3434
timeout: 10 # 超时时间,单位秒
3535
eip: false # 是否固定出口IP
36+
cls: # CLS 日志投递配置
37+
logsetId: abcdef # 所属日志集ID
38+
topicId: abcdef # 日志主题ID
3639
memorySize: 128 # 内存大小,单位MB
3740
environment: # 环境变量
3841
variables: # 环境变量数组
@@ -133,6 +136,7 @@ inputs:
133136
| environment | 否 | Object | | 函数的环境变量, 参考 [环境变量](#环境变量) |
134137
| vpcConfig | 否 | Object | | 函数的 VPC 配置, 参考 [VPC 配置](#VPC-配置) |
135138
| eip | 否 | Boolean | `false` | 是否固定出口 IP |
139+
| cls | 否 | Object | | CLS 日志投递配置,参考 [CLS 配置](#CLS-配置) |
136140

137141
##### 环境变量
138142

@@ -147,6 +151,13 @@ inputs:
147151
| subnetId | String | 子网 ID |
148152
| vpcId | String | VPC ID |
149153

154+
##### CLS 配置
155+
156+
| 参数名称 | 类型 | 描述 |
157+
| -------- | ------ | :------------ |
158+
| logsetId | String | 所属日志集 ID |
159+
| topicId | String | 日志主题 ID |
160+
150161
### API 网关配置
151162

152163
| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 |

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
"access": "public"
66
},
77
"scripts": {
8-
"int-test": "jest ./tests/integration.test.js --testEnvironment node",
9-
"test": "npm run lint && npm run prettier && npm run int-test",
8+
"test": "jest ./tests/integration.test.js --testEnvironment node",
109
"commitlint": "commitlint -f HEAD@{15}",
1110
"lint": "eslint --ext .js,.ts,.tsx .",
1211
"lint:fix": "eslint --fix --ext .js,.ts,.tsx .",

serverless.component.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: koa
2-
version: 0.0.13
2+
version: 0.1.0
33
author: 'Tencent Cloud, Inc.'
44
org: 'Tencent Cloud, Inc.'
55
description: Deploy a serverless Koa.js application onto Tencent SCF and API Gateway.

src/_shims/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"dependencies": {
3-
"tencent-serverless-http": "^1.2.0"
3+
"tencent-serverless-http": "^1.3.1"
44
}
55
}

src/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"dependencies": {
33
"download": "^8.0.0",
4-
"tencent-component-toolkit": "^1.16.4",
4+
"tencent-component-toolkit": "^1.16.8",
55
"type": "^2.1.0"
66
}
77
}

src/serverless.js

Lines changed: 115 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const { Component } = require('@serverless/core')
2-
const { MultiApigw, Scf, Apigw, Cns } = require('tencent-component-toolkit')
2+
const { Scf, Apigw, Cns } = require('tencent-component-toolkit')
33
const { TypeError } = require('tencent-component-toolkit/src/utils/error')
4-
const { uploadCodeToCos, getDefaultProtocol, deleteRecord, prepareInputs } = require('./utils')
4+
const { uploadCodeToCos, getDefaultProtocol, prepareInputs, deepClone } = require('./utils')
55
const CONFIGS = require('./config')
66

77
class ServerlessComponent extends Component {
@@ -27,135 +27,141 @@ class ServerlessComponent extends Component {
2727
}
2828

2929
async deployFunction(credentials, inputs, regionList) {
30-
const uploadCodeHandler = []
3130
const outputs = {}
3231
const appId = this.getAppId()
3332

34-
for (let eveRegionIndex = 0; eveRegionIndex < regionList.length; eveRegionIndex++) {
35-
const curRegion = regionList[eveRegionIndex]
36-
const funcDeployer = async () => {
37-
const code = await uploadCodeToCos(this, appId, credentials, inputs, curRegion)
38-
const scf = new Scf(credentials, curRegion)
39-
const tempInputs = {
40-
...inputs,
41-
code
42-
}
43-
const scfOutput = await scf.deploy(tempInputs)
44-
outputs[curRegion] = {
45-
functionName: scfOutput.FunctionName,
46-
runtime: scfOutput.Runtime,
47-
namespace: scfOutput.Namespace
48-
}
49-
50-
this.state[curRegion] = {
51-
...(this.state[curRegion] ? this.state[curRegion] : {}),
52-
...outputs[curRegion]
53-
}
33+
const funcDeployer = async (curRegion) => {
34+
const code = await uploadCodeToCos(this, appId, credentials, inputs, curRegion)
35+
const scf = new Scf(credentials, curRegion)
36+
const tempInputs = {
37+
...inputs,
38+
code
39+
}
40+
const scfOutput = await scf.deploy(deepClone(tempInputs))
41+
outputs[curRegion] = {
42+
functionName: scfOutput.FunctionName,
43+
runtime: scfOutput.Runtime,
44+
namespace: scfOutput.Namespace
45+
}
5446

55-
// default version is $LATEST
56-
outputs[curRegion].lastVersion = scfOutput.LastVersion
57-
? scfOutput.LastVersion
58-
: this.state.lastVersion || '$LATEST'
59-
60-
// default traffic is 1.0, it can also be 0, so we should compare to undefined
61-
outputs[curRegion].traffic =
62-
scfOutput.Traffic !== undefined
63-
? scfOutput.Traffic
64-
: this.state.traffic !== undefined
65-
? this.state.traffic
66-
: 1
67-
68-
if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) {
69-
outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion
70-
this.state.configTrafficVersion = scfOutput.ConfigTrafficVersion
71-
}
47+
this.state[curRegion] = {
48+
...(this.state[curRegion] ? this.state[curRegion] : {}),
49+
...outputs[curRegion]
50+
}
7251

73-
this.state.lastVersion = outputs[curRegion].lastVersion
74-
this.state.traffic = outputs[curRegion].traffic
52+
// default version is $LATEST
53+
outputs[curRegion].lastVersion = scfOutput.LastVersion
54+
? scfOutput.LastVersion
55+
: this.state.lastVersion || '$LATEST'
56+
57+
// default traffic is 1.0, it can also be 0, so we should compare to undefined
58+
outputs[curRegion].traffic =
59+
scfOutput.Traffic !== undefined
60+
? scfOutput.Traffic
61+
: this.state.traffic !== undefined
62+
? this.state.traffic
63+
: 1
64+
65+
if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) {
66+
outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion
67+
this.state.configTrafficVersion = scfOutput.ConfigTrafficVersion
7568
}
76-
uploadCodeHandler.push(funcDeployer())
69+
70+
this.state.lastVersion = outputs[curRegion].lastVersion
71+
this.state.traffic = outputs[curRegion].traffic
72+
}
73+
74+
for (let i = 0; i < regionList.length; i++) {
75+
const curRegion = regionList[i]
76+
await funcDeployer(curRegion)
7777
}
78-
await Promise.all(uploadCodeHandler)
7978
this.save()
8079
return outputs
8180
}
8281

82+
// try to add dns record
83+
async tryToAddDnsRecord(credentials, customDomains) {
84+
try {
85+
const cns = new Cns(credentials)
86+
for (let i = 0; i < customDomains.length; i++) {
87+
const item = customDomains[i]
88+
if (item.domainPrefix) {
89+
await cns.deploy({
90+
domain: item.subDomain.replace(`${item.domainPrefix}.`, ''),
91+
records: [
92+
{
93+
subDomain: item.domainPrefix,
94+
recordType: 'CNAME',
95+
recordLine: '默认',
96+
value: item.cname,
97+
ttl: 600,
98+
mx: 10,
99+
status: 'enable'
100+
}
101+
]
102+
})
103+
}
104+
}
105+
} catch (e) {
106+
console.log('METHOD_tryToAddDnsRecord', e.message)
107+
}
108+
}
109+
83110
async deployApigateway(credentials, inputs, regionList) {
84111
if (inputs.isDisabled) {
85112
return {}
86113
}
87-
const apigw = new MultiApigw(credentials, regionList)
88-
const oldState = this.state[regionList[0]] || {}
89-
inputs.oldState = {
90-
apiList: oldState.apiList || [],
91-
customDomains: oldState.customDomains || []
114+
115+
const getServiceId = (instance, region) => {
116+
const regionState = instance.state[region]
117+
return inputs.serviceId || (regionState && regionState.serviceId)
92118
}
93-
const apigwOutputs = await apigw.deploy(inputs)
94-
const outputs = {}
95-
Object.keys(apigwOutputs).forEach((curRegion) => {
96-
const curOutput = apigwOutputs[curRegion]
97-
outputs[curRegion] = {
98-
serviceId: curOutput.serviceId,
99-
subDomain: curOutput.subDomain,
100-
environment: curOutput.environment,
101-
url: `${getDefaultProtocol(inputs.protocols)}://${curOutput.subDomain}/${
102-
curOutput.environment
103-
}/`
104-
}
105-
if (curOutput.customDomains) {
106-
outputs[curRegion].customDomains = curOutput.customDomains
107-
}
108-
this.state[curRegion] = {
109-
created: curOutput.created,
110-
...(this.state[curRegion] ? this.state[curRegion] : {}),
111-
...outputs[curRegion],
112-
apiList: curOutput.apiList
113-
}
114-
})
115-
this.save()
116-
return outputs
117-
}
118119

119-
async deployCns(credentials, inputs, regionList, apigwOutputs) {
120-
const cns = new Cns(credentials)
121-
const cnsRegion = {}
120+
const deployTasks = []
121+
const outputs = {}
122122
regionList.forEach((curRegion) => {
123-
const curApigwOutput = apigwOutputs[curRegion]
124-
cnsRegion[curRegion] = curApigwOutput.subDomain
125-
})
123+
const apigwDeployer = async () => {
124+
const apigw = new Apigw(credentials, curRegion)
126125

127-
const state = []
128-
const outputs = {}
129-
const tempJson = {}
130-
for (let i = 0; i < inputs.length; i++) {
131-
const curCns = inputs[i]
132-
for (let j = 0; j < curCns.records.length; j++) {
133-
curCns.records[j].value =
134-
cnsRegion[curCns.records[j].value.replace('temp_value_about_', '')]
135-
}
136-
const tencentCnsOutputs = await cns.deploy(curCns)
137-
outputs[curCns.domain] = tencentCnsOutputs.DNS
138-
? tencentCnsOutputs.DNS
139-
: 'The domain name has already been added.'
140-
tencentCnsOutputs.domain = curCns.domain
141-
state.push(tencentCnsOutputs)
142-
}
126+
const oldState = this.state[curRegion] || {}
127+
const apigwInputs = {
128+
...inputs,
129+
oldState: {
130+
apiList: oldState.apiList || [],
131+
customDomains: oldState.customDomains || []
132+
}
133+
}
134+
// different region deployment has different service id
135+
apigwInputs.serviceId = getServiceId(this, curRegion)
136+
const apigwOutput = await apigw.deploy(deepClone(apigwInputs))
137+
outputs[curRegion] = {
138+
serviceId: apigwOutput.serviceId,
139+
subDomain: apigwOutput.subDomain,
140+
environment: apigwOutput.environment,
141+
url: `${getDefaultProtocol(inputs.protocols)}://${apigwOutput.subDomain}/${
142+
apigwOutput.environment
143+
}/`
144+
}
143145

144-
// 删除serverless创建的但是不在本次列表中
145-
try {
146-
for (let i = 0; i < state.length; i++) {
147-
tempJson[state[i].domain] = state[i].records
148-
}
149-
const recordHistory = this.state.cns || []
150-
for (let i = 0; i < recordHistory.length; i++) {
151-
const delList = deleteRecord(tempJson[recordHistory[i].domain], recordHistory[i].records)
152-
if (delList && delList.length > 0) {
153-
await cns.remove({ deleteList: delList })
146+
if (apigwOutput.customDomains) {
147+
// TODO: need confirm add cns authentication
148+
if (inputs.autoAddDnsRecord === true) {
149+
// await this.tryToAddDnsRecord(credentials, apigwOutput.customDomains)
150+
}
151+
outputs[curRegion].customDomains = apigwOutput.customDomains
152+
}
153+
this.state[curRegion] = {
154+
created: true,
155+
...(this.state[curRegion] ? this.state[curRegion] : {}),
156+
...outputs[curRegion],
157+
apiList: apigwOutput.apiList
154158
}
155159
}
156-
} catch (e) {}
160+
deployTasks.push(apigwDeployer())
161+
})
162+
163+
await Promise.all(deployTasks)
157164

158-
this.state['cns'] = state
159165
this.save()
160166
return outputs
161167
}
@@ -166,7 +172,7 @@ class ServerlessComponent extends Component {
166172
const credentials = this.getCredentials()
167173

168174
// 对Inputs内容进行标准化
169-
const { regionList, functionConf, apigatewayConf, cnsConf } = await prepareInputs(
175+
const { regionList, functionConf, apigatewayConf } = await prepareInputs(
170176
this,
171177
credentials,
172178
inputs
@@ -198,11 +204,6 @@ class ServerlessComponent extends Component {
198204
outputs['scf'] = functionOutputs
199205
}
200206

201-
// cns depends on apigw, so if disabled apigw, just ignore it.
202-
if (cnsConf.length > 0 && apigatewayConf.isDisabled !== true) {
203-
outputs['cns'] = await this.deployCns(credentials, cnsConf, regionList, apigwOutputs)
204-
}
205-
206207
this.state.region = regionList[0]
207208
this.state.regionList = regionList
208209
this.state.lambdaArn = functionConf.name

0 commit comments

Comments
 (0)