Skip to content

Commit 8815e40

Browse files
committed
feat: add state store, and remove method
1 parent 90a4339 commit 8815e40

File tree

6 files changed

+168
-86
lines changed

6 files changed

+168
-86
lines changed

.editorconfig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# EditorConfig is awesome: http://EditorConfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
[*]
7+
charset = utf-8
8+
end_of_line = lf
9+
insert_final_newline = true
10+
indent_size = 2
11+
indent_style = space
12+
trim_trailing_whitespace = true
13+
14+
[*.md]
15+
trim_trailing_whitespace = false

example/serverless.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
component: express-tencent@dev # (required) name of the component. In that case, it's express.
2-
name: express-apixxxxxxxxxx # (required) name of your express component instance.
2+
name: express-api # (required) name of your express component instance.
33
org: test # (optional) serverless dashboard org. default is the first org you created during signup.
4-
app: myAppxxxx # (optional) serverless dashboard app. default is the same as the name property.
4+
app: expressApp # (optional) serverless dashboard app. default is the same as the name property.
55
stage: dev # (optional) serverless dashboard stage. default is dev.
66

77
inputs:

src/_express/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
"author": "Serverless, Inc.",
1313
"license": "Apache",
1414
"dependencies": {
15-
"tencent-serverless-http": "^1.0.5"
15+
"tencent-serverless-http": "^1.0.7"
1616
}
1717
}

src/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"license": "Apache",
1414
"dependencies": {
1515
"fs-extra": "^8.1.0",
16-
"tencent-component-toolkit": "^1.0.1",
16+
"tencent-component-toolkit": "^1.0.4",
1717
"type": "^2.0.0"
1818
}
1919
}

src/serverless.js

Lines changed: 146 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
const { Component } = require('@serverless/core')
2+
const ensureObject = require('type/object/ensure')
23
const ensureIterable = require('type/iterable/ensure')
34
const ensureString = require('type/string/ensure')
4-
const { MultiApigw, Scf, Cos, Cns } = require('tencent-component-toolkit')
5+
const { MultiApigw, Scf, Apigw, Cos, Cns } = require('tencent-component-toolkit')
56
const { packageExpress, generateId } = require('./utils')
67

78
const DEFAULTS = {
89
handler: 'sl_handler.handler',
910
runtime: 'Nodejs8.9',
1011
exclude: ['.git/**', '.gitignore', '.DS_Store'],
1112
timeout: 3,
12-
memorySize: 128
13+
memorySize: 128,
14+
namespace: 'default'
1315
}
1416

1517
class Express extends Component {
18+
getDefaultProtocol(protocols) {
19+
if (protocols.map((i) => i.toLowerCase()).includes('https')) {
20+
return 'https'
21+
}
22+
return 'http'
23+
}
24+
1625
mergeJson(sourceJson, targetJson) {
1726
for (const eveKey in sourceJson) {
1827
if (targetJson.hasOwnProperty(eveKey)) {
@@ -48,39 +57,59 @@ class Express extends Component {
4857

4958
async prepareInputs(inputs = {}) {
5059
// 对function inputs进行标准化
51-
const tempFunctionConf = inputs.functionConf ? inputs.functionConf : undefined
60+
const tempFunctionConf = inputs.functionConf ? inputs.functionConf : {}
5261
const fromClientRemark = `tencent-express`
62+
const regionList = inputs.region
63+
? typeof inputs.region == 'string'
64+
? [inputs.region]
65+
: inputs.region
66+
: ['ap-guangzhou']
67+
68+
// chenck state function name
69+
const stateFunctionName = this.state[regionList[0]] && this.state[regionList[0]].functionName
70+
// check state service id
71+
const stateServiceId = this.state[regionList[0]] && this.state[regionList[0]].serviceId
5372

5473
const functionConf = {
74+
code:
75+
typeof inputs.src === 'object'
76+
? inputs.src
77+
: {
78+
src: inputs.src
79+
},
5580
name:
5681
ensureString(inputs.functionName, { isOptional: true }) ||
57-
this.state.functionName ||
82+
stateFunctionName ||
5883
`express_component_${generateId()}`,
59-
code:
60-
ensureString(tempFunctionConf && tempFunctionConf.src ? tempFunctionConf.src : inputs.src, {
61-
isOptional: true
62-
}) || process.cwd(),
63-
region: inputs.region
64-
? typeof inputs.region == 'string'
65-
? [inputs.region]
66-
: inputs.region
67-
: ['ap-guangzhou'],
68-
handler: ensureString(
69-
tempFunctionConf && tempFunctionConf.handler ? tempFunctionConf.handler : inputs.handler,
70-
{ default: DEFAULTS.handler }
84+
region: regionList,
85+
handler: ensureString(tempFunctionConf.handler ? tempFunctionConf.handler : inputs.handler, {
86+
default: DEFAULTS.handler
87+
}),
88+
runtime: ensureString(tempFunctionConf.runtime ? tempFunctionConf.runtime : inputs.runtime, {
89+
default: DEFAULTS.runtime
90+
}),
91+
namespace: ensureString(
92+
tempFunctionConf.namespace ? tempFunctionConf.namespace : inputs.namespace,
93+
{ default: DEFAULTS.namespace }
7194
),
72-
runtime: ensureString(
73-
tempFunctionConf && tempFunctionConf.runtime ? tempFunctionConf.runtime : inputs.runtime,
74-
{ default: DEFAULTS.runtime }
95+
description: ensureString(
96+
tempFunctionConf.description ? tempFunctionConf.description : inputs.description,
97+
{
98+
default: DEFAULTS.description
99+
}
75100
),
76101
fromClientRemark
77102
}
103+
functionConf.tags = ensureObject(tempFunctionConf.tags ? tempFunctionConf.tags : inputs.tag, {
104+
default: null
105+
})
106+
78107
functionConf.include = ensureIterable(
79-
tempFunctionConf && tempFunctionConf.include ? tempFunctionConf.include : inputs.include,
108+
tempFunctionConf.include ? tempFunctionConf.include : inputs.include,
80109
{ default: [], ensureItem: ensureString }
81110
)
82111
functionConf.exclude = ensureIterable(
83-
tempFunctionConf && tempFunctionConf.exclude ? tempFunctionConf.exclude : inputs.exclude,
112+
tempFunctionConf.exclude ? tempFunctionConf.exclude : inputs.exclude,
84113
{ default: [], ensureItem: ensureString }
85114
)
86115
functionConf.exclude.push('.git/**', '.gitignore', '.serverless', '.DS_Store')
@@ -104,7 +133,7 @@ class Express extends Component {
104133
apigatewayConf.fromClientRemark = fromClientRemark
105134
apigatewayConf.serviceName = inputs.serviceName
106135
apigatewayConf.description = `Serverless Framework Tencent-Express Component`
107-
apigatewayConf.serviceId = inputs.serviceId
136+
apigatewayConf.serviceId = inputs.serviceId || stateServiceId
108137
apigatewayConf.region = functionConf.region
109138
apigatewayConf.protocols = apigatewayConf.protocols || ['http']
110139
apigatewayConf.environment = apigatewayConf.environment ? apigatewayConf.environment : 'release'
@@ -179,37 +208,36 @@ class Express extends Component {
179208
}
180209

181210
return {
182-
region: functionConf.region,
183-
functionConf: functionConf,
184-
apigatewayConf: apigatewayConf,
185-
cnsConf: cnsConf
211+
regionList,
212+
functionConf,
213+
apigatewayConf,
214+
cnsConf
186215
}
187216
}
188217

189218
async uploadCodeToCos(credentials, inputs, region, filePath) {
190219
// 创建cos对象
191220
const cos = new Cos(credentials, region)
192221
// 创建存储桶 + 设置生命周期
193-
if (!inputs.code || !inputs.code.bucket) {
194-
inputs.code = {}
222+
if (!inputs.code.bucket) {
195223
inputs.code.bucket = `sls-cloudfunction-${region}-code`
196224
await cos.deploy({
197225
bucket: inputs.code.bucket,
198-
force: true
199-
// lifecycle: [
200-
// {
201-
// status: 'Enabled',
202-
// id: 'deleteObject',
203-
// filter: '',
204-
// expiration: { days: '10' },
205-
// abortIncompleteMultipartUpload: { daysAfterInitiation: '10' }
206-
// }
207-
// ]
226+
force: true,
227+
lifecycle: [
228+
{
229+
status: 'Enabled',
230+
id: 'deleteObject',
231+
filter: '',
232+
expiration: { days: '10' },
233+
abortIncompleteMultipartUpload: { daysAfterInitiation: '10' }
234+
}
235+
]
208236
})
209237
}
210238

211239
// 上传代码
212-
if (!inputs.code || inputs.code.object) {
240+
if (!inputs.code.object) {
213241
const object = `${inputs.name}-${Math.floor(Date.now() / 1000)}.zip`
214242
inputs.code.object = object
215243
await cos.upload({
@@ -218,39 +246,46 @@ class Express extends Component {
218246
key: inputs.code.object
219247
})
220248
}
249+
this.state.bucket = inputs.code.bucket
250+
this.state.object = inputs.code.object
251+
221252
return {
222253
bucket: inputs.code.bucket,
223254
object: inputs.code.object
224255
}
225256
}
226257

227258
async deployFunction(credentials, inputs, regionList) {
228-
// 打包代码
229-
// todo 打包这里还没有仔细看,可能这样用的不对
230-
const packageDir = await packageExpress(this, inputs)
259+
// if set bucket and object not pack code
260+
let packageDir
261+
if (!inputs.code.bucket || !inputs.code.object) {
262+
packageDir = await packageExpress(this, inputs)
263+
}
231264

232265
// 上传代码到COS
233266
const uploadCodeHandler = []
234267
const outputs = {}
268+
235269
for (let eveRegionIndex = 0; eveRegionIndex < regionList.length; eveRegionIndex++) {
270+
const curRegion = regionList[eveRegionIndex]
236271
const funcDeployer = async () => {
237-
const { bucket, object } = await this.uploadCodeToCos(
238-
credentials,
239-
inputs,
240-
regionList[eveRegionIndex],
241-
packageDir
242-
)
243-
const scf = new Scf(credentials, regionList[eveRegionIndex])
272+
const code = await this.uploadCodeToCos(credentials, inputs, curRegion, packageDir)
273+
const scf = new Scf(credentials, curRegion)
244274
const tempInputs = {
245275
...inputs,
246-
code: {
247-
bucket,
248-
object
249-
}
276+
code
277+
}
278+
const scfOutput = await scf.deploy(tempInputs)
279+
outputs[curRegion] = {
280+
functionName: scfOutput.FunctionName,
281+
runtime: scfOutput.Runtime,
282+
namespace: scfOutput.Namespace
250283
}
251-
console.log('tempInputs', tempInputs)
252284

253-
outputs[regionList[eveRegionIndex]] = await scf.deploy(tempInputs)
285+
this.state[curRegion] = {
286+
...(this.state[curRegion] ? this.state[curRegion] : {}),
287+
...outputs[curRegion]
288+
}
254289
}
255290
uploadCodeHandler.push(funcDeployer())
256291
}
@@ -261,7 +296,31 @@ class Express extends Component {
261296

262297
async deployApigateway(credentials, inputs, regionList) {
263298
const apigw = new MultiApigw(credentials, regionList)
264-
const outputs = await apigw.deploy(inputs)
299+
inputs.oldState = {
300+
apiList: (this.state[regionList[0]] && this.state[regionList[0]].apiList) || []
301+
}
302+
const apigwOutputs = await apigw.deploy(inputs)
303+
const outputs = {}
304+
Object.keys(apigwOutputs).forEach((curRegion) => {
305+
const curOutput = apigwOutputs[curRegion]
306+
outputs[curRegion] = {
307+
serviceId: curOutput.serviceId,
308+
subDomain: curOutput.subDomain,
309+
environment: curOutput.environment,
310+
url: `${this.getDefaultProtocol(inputs.protocols)}://${curOutput.subDomain}/${
311+
curOutput.environment
312+
}/`
313+
}
314+
if (curOutput.customDomains) {
315+
outputs[curRegion].customDomains = curOutput.customDomains
316+
}
317+
this.state[curRegion] = {
318+
created: curOutput.created,
319+
...(this.state[curRegion] ? this.state[curRegion] : {}),
320+
...outputs[curRegion],
321+
apiList: curOutput.apiList
322+
}
323+
})
265324
return outputs
266325
}
267326

@@ -271,20 +330,13 @@ class Express extends Component {
271330
}
272331

273332
async deploy(inputs) {
274-
console.log('++++++++++')
275-
console.log('state', this.state)
276-
console.log('++++++++++')
277-
278333
console.log(`Deploying Express App...`)
279334

280335
// 获取腾讯云密钥信息
281336
const credentials = this.credentials.tencent
282337

283338
// 对Inputs内容进行标准化
284-
const { region, functionConf, apigatewayConf, cnsConf } = await this.prepareInputs(inputs)
285-
286-
// 获取地域列表
287-
const regionList = typeof inputs.region == 'string' ? [inputs.region] : inputs.region
339+
const { regionList, functionConf, apigatewayConf, cnsConf } = await this.prepareInputs(inputs)
288340

289341
// 部署函数 + API网关
290342
const outputs = {}
@@ -307,21 +359,36 @@ class Express extends Component {
307359
return outputs
308360
}
309361

310-
async remove() {
311-
// const clients = getClients(
312-
// process.env.SERVERLESS_PLATFORM_VENDOR === 'tencent'
313-
// ? this.credentials.tencent
314-
// : this.credentials.aws,
315-
// this.state.region
316-
// )
317-
//
318-
// await removeAllRoles(this, clients)
319-
// await removeLambda(this, clients)
320-
// await removeDomain(this, clients)
321-
// await removeApi(this, clients)
322-
//
323-
// this.state = {}
324-
// return {}
362+
async remove(inputs = {}) {
363+
console.log(`Removing Express App...`)
364+
365+
const { regionList } = await this.prepareInputs(inputs)
366+
const { state } = this
367+
const credentials = this.credentials.tencent
368+
const removeHandlers = []
369+
for (let i = 0; i < regionList.length; i++) {
370+
const curRegion = regionList[i]
371+
const curState = state[curRegion]
372+
const scf = new Scf(credentials, curRegion)
373+
const apigw = new Apigw(credentials, curRegion)
374+
const handler = async () => {
375+
await scf.remove({
376+
functionName: curState.functionName,
377+
namespace: curState.namespace
378+
})
379+
await apigw.remove({
380+
created: curState.created,
381+
environment: curState.environment,
382+
serviceId: curState.serviceId,
383+
apiList: curState.apiList,
384+
customDomains: curState.customDomains
385+
})
386+
}
387+
removeHandlers.push(handler())
388+
}
389+
390+
await Promise.all(removeHandlers)
391+
this.state = {}
325392
}
326393
}
327394

0 commit comments

Comments
 (0)