Skip to content

Commit 33ae212

Browse files
committed
fix: update deploy flow for multi region
1 parent 45412f0 commit 33ae212

File tree

4 files changed

+211
-287
lines changed

4 files changed

+211
-287
lines changed

serverless.component.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: express
2-
version: 0.1.4
2+
version: 0.1.5
33
author: Tencent Cloud, Inc.
44
org: Tencent Cloud, Inc.
55
description: Deploy a serverless Express.js application on Tencent SCF and API Gateway.

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.2",
4+
"tencent-component-toolkit": "^1.16.4",
55
"type": "^2.0.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, Cam, Metrics } = require('tencent-component-toolkit')
2+
const { Scf, Apigw, Cns, Cam, Metrics } = 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 {
@@ -39,135 +39,141 @@ class ServerlessComponent extends Component {
3939
}
4040
}
4141

42-
const uploadCodeHandler = []
4342
const outputs = {}
4443
const appId = this.getAppId()
4544

46-
for (let eveRegionIndex = 0; eveRegionIndex < regionList.length; eveRegionIndex++) {
47-
const curRegion = regionList[eveRegionIndex]
48-
const funcDeployer = async () => {
49-
const code = await uploadCodeToCos(this, appId, credentials, inputs, curRegion)
50-
const scf = new Scf(credentials, curRegion)
51-
const tempInputs = {
52-
...inputs,
53-
code
54-
}
55-
const scfOutput = await scf.deploy(tempInputs)
56-
outputs[curRegion] = {
57-
functionName: scfOutput.FunctionName,
58-
runtime: scfOutput.Runtime,
59-
namespace: scfOutput.Namespace
60-
}
61-
62-
this.state[curRegion] = {
63-
...(this.state[curRegion] ? this.state[curRegion] : {}),
64-
...outputs[curRegion]
65-
}
45+
const funcDeployer = async (curRegion) => {
46+
const code = await uploadCodeToCos(this, appId, credentials, inputs, curRegion)
47+
const scf = new Scf(credentials, curRegion)
48+
const tempInputs = {
49+
...inputs,
50+
code
51+
}
52+
const scfOutput = await scf.deploy(deepClone(tempInputs))
53+
outputs[curRegion] = {
54+
functionName: scfOutput.FunctionName,
55+
runtime: scfOutput.Runtime,
56+
namespace: scfOutput.Namespace
57+
}
6658

67-
// default version is $LATEST
68-
outputs[curRegion].lastVersion = scfOutput.LastVersion
69-
? scfOutput.LastVersion
70-
: this.state.lastVersion || '$LATEST'
71-
72-
// default traffic is 1.0, it can also be 0, so we should compare to undefined
73-
outputs[curRegion].traffic =
74-
scfOutput.Traffic !== undefined
75-
? scfOutput.Traffic
76-
: this.state.traffic !== undefined
77-
? this.state.traffic
78-
: 1
79-
80-
if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) {
81-
outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion
82-
this.state.configTrafficVersion = scfOutput.ConfigTrafficVersion
83-
}
59+
this.state[curRegion] = {
60+
...(this.state[curRegion] ? this.state[curRegion] : {}),
61+
...outputs[curRegion]
62+
}
8463

85-
this.state.lastVersion = outputs[curRegion].lastVersion
86-
this.state.traffic = outputs[curRegion].traffic
64+
// default version is $LATEST
65+
outputs[curRegion].lastVersion = scfOutput.LastVersion
66+
? scfOutput.LastVersion
67+
: this.state.lastVersion || '$LATEST'
68+
69+
// default traffic is 1.0, it can also be 0, so we should compare to undefined
70+
outputs[curRegion].traffic =
71+
scfOutput.Traffic !== undefined
72+
? scfOutput.Traffic
73+
: this.state.traffic !== undefined
74+
? this.state.traffic
75+
: 1
76+
77+
if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) {
78+
outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion
79+
this.state.configTrafficVersion = scfOutput.ConfigTrafficVersion
8780
}
88-
uploadCodeHandler.push(funcDeployer())
81+
82+
this.state.lastVersion = outputs[curRegion].lastVersion
83+
this.state.traffic = outputs[curRegion].traffic
84+
}
85+
86+
for (let i = 0; i < regionList.length; i++) {
87+
const curRegion = regionList[i]
88+
await funcDeployer(curRegion)
8989
}
90-
await Promise.all(uploadCodeHandler)
9190
this.save()
9291
return outputs
9392
}
9493

94+
// try to add dns record
95+
async tryToAddDnsRecord(credentials, customDomains) {
96+
try {
97+
const cns = new Cns(credentials)
98+
for (let i = 0; i < customDomains.length; i++) {
99+
const item = customDomains[i]
100+
if (item.domainPrefix) {
101+
await cns.deploy({
102+
domain: item.subDomain.replace(`${item.domainPrefix}.`, ''),
103+
records: [
104+
{
105+
subDomain: item.domainPrefix,
106+
recordType: 'CNAME',
107+
recordLine: '默认',
108+
value: item.cname,
109+
ttl: 600,
110+
mx: 10,
111+
status: 'enable'
112+
}
113+
]
114+
})
115+
}
116+
}
117+
} catch (e) {
118+
console.log('METHOD_tryToAddDnsRecord', e.message)
119+
}
120+
}
121+
95122
async deployApigateway(credentials, inputs, regionList) {
96123
if (inputs.isDisabled) {
97124
return {}
98125
}
99-
const apigw = new MultiApigw(credentials, regionList)
100-
const oldState = this.state[regionList[0]] || {}
101-
inputs.oldState = {
102-
apiList: oldState.apiList || [],
103-
customDomains: oldState.customDomains || []
126+
127+
const getServiceId = (instance, region) => {
128+
const regionState = instance.state[region]
129+
return inputs.serviceId || (regionState && regionState.serviceId)
104130
}
105-
const apigwOutputs = await apigw.deploy(inputs)
106-
const outputs = {}
107-
Object.keys(apigwOutputs).forEach((curRegion) => {
108-
const curOutput = apigwOutputs[curRegion]
109-
outputs[curRegion] = {
110-
serviceId: curOutput.serviceId,
111-
subDomain: curOutput.subDomain,
112-
environment: curOutput.environment,
113-
url: `${getDefaultProtocol(inputs.protocols)}://${curOutput.subDomain}/${
114-
curOutput.environment
115-
}/`
116-
}
117-
if (curOutput.customDomains) {
118-
outputs[curRegion].customDomains = curOutput.customDomains
119-
}
120-
this.state[curRegion] = {
121-
created: curOutput.created,
122-
...(this.state[curRegion] ? this.state[curRegion] : {}),
123-
...outputs[curRegion],
124-
apiList: curOutput.apiList
125-
}
126-
})
127-
this.save()
128-
return outputs
129-
}
130131

131-
async deployCns(credentials, inputs, regionList, apigwOutputs) {
132-
const cns = new Cns(credentials)
133-
const cnsRegion = {}
132+
const deployTasks = []
133+
const outputs = {}
134134
regionList.forEach((curRegion) => {
135-
const curApigwOutput = apigwOutputs[curRegion]
136-
cnsRegion[curRegion] = curApigwOutput.subDomain
137-
})
135+
const apigwDeployer = async () => {
136+
const apigw = new Apigw(credentials, curRegion)
138137

139-
const state = []
140-
const outputs = {}
141-
const tempJson = {}
142-
for (let i = 0; i < inputs.length; i++) {
143-
const curCns = inputs[i]
144-
for (let j = 0; j < curCns.records.length; j++) {
145-
curCns.records[j].value =
146-
cnsRegion[curCns.records[j].value.replace('temp_value_about_', '')]
147-
}
148-
const tencentCnsOutputs = await cns.deploy(curCns)
149-
outputs[curCns.domain] = tencentCnsOutputs.DNS
150-
? tencentCnsOutputs.DNS
151-
: 'The domain name has already been added.'
152-
tencentCnsOutputs.domain = curCns.domain
153-
state.push(tencentCnsOutputs)
154-
}
138+
const oldState = this.state[curRegion] || {}
139+
const apigwInputs = {
140+
...inputs,
141+
oldState: {
142+
apiList: oldState.apiList || [],
143+
customDomains: oldState.customDomains || []
144+
}
145+
}
146+
// different region deployment has different service id
147+
apigwInputs.serviceId = getServiceId(this, curRegion)
148+
const apigwOutput = await apigw.deploy(deepClone(apigwInputs))
149+
outputs[curRegion] = {
150+
serviceId: apigwOutput.serviceId,
151+
subDomain: apigwOutput.subDomain,
152+
environment: apigwOutput.environment,
153+
url: `${getDefaultProtocol(inputs.protocols)}://${apigwOutput.subDomain}/${
154+
apigwOutput.environment
155+
}/`
156+
}
155157

156-
// 删除serverless创建的但是不在本次列表中
157-
try {
158-
for (let i = 0; i < state.length; i++) {
159-
tempJson[state[i].domain] = state[i].records
160-
}
161-
const recordHistory = this.state.cns || []
162-
for (let i = 0; i < recordHistory.length; i++) {
163-
const delList = deleteRecord(tempJson[recordHistory[i].domain], recordHistory[i].records)
164-
if (delList && delList.length > 0) {
165-
await cns.remove({ deleteList: delList })
158+
if (apigwOutput.customDomains) {
159+
// TODO: need confirm add cns authentication
160+
if (inputs.autoAddDnsRecord === true) {
161+
// await this.tryToAddDnsRecord(credentials, apigwOutput.customDomains)
162+
}
163+
outputs[curRegion].customDomains = apigwOutput.customDomains
164+
}
165+
this.state[curRegion] = {
166+
created: true,
167+
...(this.state[curRegion] ? this.state[curRegion] : {}),
168+
...outputs[curRegion],
169+
apiList: apigwOutput.apiList
166170
}
167171
}
168-
} catch (e) {}
172+
deployTasks.push(apigwDeployer())
173+
})
174+
175+
await Promise.all(deployTasks)
169176

170-
this.state['cns'] = state
171177
this.save()
172178
return outputs
173179
}
@@ -178,7 +184,7 @@ class ServerlessComponent extends Component {
178184
const credentials = this.getCredentials()
179185

180186
// 对Inputs内容进行标准化
181-
const { regionList, functionConf, apigatewayConf, cnsConf } = await prepareInputs(
187+
const { regionList, functionConf, apigatewayConf } = await prepareInputs(
182188
this,
183189
credentials,
184190
inputs
@@ -210,11 +216,6 @@ class ServerlessComponent extends Component {
210216
outputs['scf'] = functionOutputs
211217
}
212218

213-
// cns depends on apigw, so if disabled apigw, just ignore it.
214-
if (cnsConf.length > 0 && apigatewayConf.isDisabled !== true) {
215-
outputs['cns'] = await this.deployCns(credentials, cnsConf, regionList, apigwOutputs)
216-
}
217-
218219
this.state.region = regionList[0]
219220
this.state.regionList = regionList
220221
this.state.lambdaArn = functionConf.name

0 commit comments

Comments
 (0)