Skip to content

Commit fe034d1

Browse files
committed
fix: update deploy flow for multi region
1 parent 6574655 commit fe034d1

File tree

3 files changed

+220
-286
lines changed

3 files changed

+220
-286
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ inputs:
9494
通过 `sls` 命令进行部署,并可以添加 `--debug` 参数查看部署过程中的信息
9595

9696
```bash
97-
$ sls --debug
97+
$ sls deploy --debug
9898
```
9999

100100
### 4. 移除

src/serverless.js

+127-114
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, Cam } = 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,153 @@ class ServerlessComponent extends Component {
2727
}
2828

2929
async deployFunction(credentials, inputs, regionList) {
30-
const uploadCodeHandler = []
30+
if (!inputs.role) {
31+
try {
32+
const camClient = new Cam(credentials)
33+
const roleExist = await camClient.CheckSCFExcuteRole()
34+
if (roleExist) {
35+
inputs.role = 'QCS_SCFExcuteRole'
36+
}
37+
} catch (e) {
38+
// no op
39+
}
40+
}
41+
3142
const outputs = {}
3243
const appId = this.getAppId()
3344

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-
}
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+
}
5458

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-
}
59+
this.state[curRegion] = {
60+
...(this.state[curRegion] ? this.state[curRegion] : {}),
61+
...outputs[curRegion]
62+
}
7263

73-
this.state.lastVersion = outputs[curRegion].lastVersion
74-
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
7580
}
76-
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)
7789
}
78-
await Promise.all(uploadCodeHandler)
7990
this.save()
8091
return outputs
8192
}
8293

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+
83122
async deployApigateway(credentials, inputs, regionList) {
84123
if (inputs.isDisabled) {
85124
return {}
86125
}
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 || []
126+
127+
const getServiceId = (instance, region) => {
128+
const regionState = instance.state[region]
129+
return inputs.serviceId || (regionState && regionState.serviceId)
92130
}
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-
}
118131

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

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-
}
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+
}
143157

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 })
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
154170
}
155171
}
156-
} catch (e) {}
172+
deployTasks.push(apigwDeployer())
173+
})
174+
175+
await Promise.all(deployTasks)
157176

158-
this.state['cns'] = state
159177
this.save()
160178
return outputs
161179
}
@@ -166,7 +184,7 @@ class ServerlessComponent extends Component {
166184
const credentials = this.getCredentials()
167185

168186
// 对Inputs内容进行标准化
169-
const { regionList, functionConf, apigatewayConf, cnsConf } = await prepareInputs(
187+
const { regionList, functionConf, apigatewayConf } = await prepareInputs(
170188
this,
171189
credentials,
172190
inputs
@@ -198,11 +216,6 @@ class ServerlessComponent extends Component {
198216
outputs['scf'] = functionOutputs
199217
}
200218

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-
206219
this.state.region = regionList[0]
207220
this.state.regionList = regionList
208221
this.state.lambdaArn = functionConf.name

0 commit comments

Comments
 (0)