From 31bc27157b315bb69ccc3f0b5fe2ee5a752ef30a Mon Sep 17 00:00:00 2001 From: Avril Li Date: Fri, 3 Jul 2020 11:45:16 +0800 Subject: [PATCH 01/36] Add integration test (#11) * chore(test): add integration test and update isDisabled config * chore: update jest version to support node8.x * chore: remove process.env variables of test * chore: update test case --- package.json | 5 +++- src/utils.js | 1 + tests/integration.test.js | 53 +++++++++++++++++++++++++++++++++++++++ tests/utils.js | 24 ++++++++++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 tests/integration.test.js create mode 100644 tests/utils.js diff --git a/package.json b/package.json index cef867f..1b07f3f 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "access": "public" }, "scripts": { - "test": "npm run lint && npm run prettier", + "int-test": "jest ./tests/integration.test.js --testEnvironment node", + "test": "npm run lint && npm run prettier && npm run int-test", "commitlint": "commitlint -f HEAD@{15}", "lint": "eslint --ext .js,.ts,.tsx .", "lint:fix": "eslint --fix --ext .js,.ts,.tsx .", @@ -44,6 +45,7 @@ "@semantic-release/git": "^9.0.0", "@semantic-release/npm": "^7.0.4", "@semantic-release/release-notes-generator": "^9.0.1", + "@serverless/platform-client-china": "^1.0.19", "babel-eslint": "^10.1.0", "dotenv": "^8.2.0", "eslint": "^6.8.0", @@ -51,6 +53,7 @@ "eslint-plugin-import": "^2.20.1", "eslint-plugin-prettier": "^3.1.2", "husky": "^4.2.3", + "jest": "^25.0.1", "lint-staged": "^10.0.8", "prettier": "^1.19.1", "semantic-release": "^17.0.4" diff --git a/src/utils.js b/src/utils.js index d32b376..0bf0264 100644 --- a/src/utils.js +++ b/src/utils.js @@ -267,6 +267,7 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { // 对apigw inputs进行标准化 const apigatewayConf = inputs.apigatewayConf ? inputs.apigatewayConf : {} + apigatewayConf.isDisabled = apigatewayConf.isDisabled === true apigatewayConf.fromClientRemark = fromClientRemark apigatewayConf.serviceName = inputs.serviceName apigatewayConf.description = `Serverless Framework Tencent-${capitalString( diff --git a/tests/integration.test.js b/tests/integration.test.js new file mode 100644 index 0000000..4d35f2b --- /dev/null +++ b/tests/integration.test.js @@ -0,0 +1,53 @@ +const { generateId, getServerlessSdk } = require('./utils') + +// set enough timeout for deployment to finish +jest.setTimeout(300000) + +// the yaml file we're testing against +const instanceYaml = { + org: 'orgDemo', + app: 'appDemo', + component: 'flask@dev', + name: `flask-integration-tests-${generateId()}`, + stage: 'dev', + inputs: { + runtime: 'Python3.6', + region: 'ap-guangzhou', + apigatewayConf: { environment: 'test' } + } +} + +// get credentials from process.env but need to init empty credentials object +const credentials = { + tencent: {} +} + +// get serverless construct sdk +const sdk = getServerlessSdk(instanceYaml.org) + +it('should successfully deploy flask app', async () => { + const instance = await sdk.deploy(instanceYaml, { tencent: {} }) + expect(instance).toBeDefined() + expect(instance.instanceName).toEqual(instanceYaml.name) + // get src from template by default + expect(instance.outputs.templateUrl).toBeDefined() + expect(instance.outputs).toBeDefined() + expect(instance.outputs.region).toEqual(instanceYaml.inputs.region) + expect(instance.outputs.scf).toBeDefined() + expect(instance.outputs.scf.runtime).toEqual(instanceYaml.inputs.runtime) + expect(instance.outputs.apigw).toBeDefined() + expect(instance.outputs.apigw.environment).toEqual(instanceYaml.inputs.apigatewayConf.environment) +}) + +it('should successfully remove flask app', async () => { + await sdk.remove(instanceYaml, credentials) + result = await sdk.getInstance( + instanceYaml.org, + instanceYaml.stage, + instanceYaml.app, + instanceYaml.name + ) + + // remove action won't delete the service cause the apigw have the api binded + expect(result.instance.instanceStatus).toEqual('inactive') +}) diff --git a/tests/utils.js b/tests/utils.js new file mode 100644 index 0000000..d047afa --- /dev/null +++ b/tests/utils.js @@ -0,0 +1,24 @@ +const { ServerlessSDK } = require('@serverless/platform-client-china') + +/* + * Generate random id + */ +const generateId = () => + Math.random() + .toString(36) + .substring(6) + +/* + * Initializes and returns an instance of the serverless sdk + * @param ${string} orgName - the serverless org name. + */ +const getServerlessSdk = (orgName) => { + const sdk = new ServerlessSDK({ + context: { + orgName + } + }) + return sdk +} + +module.exports = { generateId, getServerlessSdk } From 661bd449a7c51801163f537e4ea12837542f119b Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 6 Jul 2020 19:41:59 +0800 Subject: [PATCH 02/36] feat: support scf publish version and traffic setup --- example/serverless.yml | 2 +- serverless.component.yml | 4 +-- src/config.js | 2 +- src/package.json | 13 +------- src/serverless.js | 20 +++++++++++++ src/utils.js | 65 +++++++++++++++++++++++++++------------- 6 files changed, 69 insertions(+), 37 deletions(-) diff --git a/example/serverless.yml b/example/serverless.yml index 18d6dc2..b28ba97 100644 --- a/example/serverless.yml +++ b/example/serverless.yml @@ -1,7 +1,7 @@ org: orgDemo # (optional) serverless dashboard org. default is the first org you created during signup. app: appDemo # (optional) serverless dashboard app. default is the same as the name property. stage: dev # (optional) serverless dashboard stage. default is dev. -component: flask # (required) name of the component. In that case, it's flask. +component: flask@dev # (required) name of the component. In that case, it's flask. name: flashDemo # (required) name of your flash component instance. inputs: diff --git a/serverless.component.yml b/serverless.component.yml index 0c3f0f8..20d288f 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -2,9 +2,9 @@ name: flask version: 0.0.1 author: Tencent Cloud, Inc org: Tencent Cloud, Inc -description: Deploys a serverless Flask application onto Tencent SCF and Tencent APIGateway. +description: Deploy a serverless Flask application onto Tencent SCF and APIGateway. keywords: tencent, serverless, flask repo: https://github.com/serverless-components/tencent-flask -readme: https://github.com/serverless-components/tencent-flask/tree/v2/README.md +readme: https://github.com/serverless-components/tencent-flask/tree/master/README.md license: MIT main: ./src diff --git a/src/config.js b/src/config.js index c8ef45c..d92282e 100644 --- a/src/config.js +++ b/src/config.js @@ -8,7 +8,7 @@ const CONFIGS = { timeout: 3, memorySize: 128, namespace: 'default', - description: 'Function created by serverless component' + description: 'Created by Serverless Component' } module.exports = CONFIGS diff --git a/src/package.json b/src/package.json index 26358e9..157a43e 100644 --- a/src/package.json +++ b/src/package.json @@ -1,18 +1,7 @@ { - "name": "@serverless/flask", - "main": "./serverless.js", - "publishConfig": { - "access": "public" - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "lint": "eslint . --fix --cache" - }, - "author": "Tencent Cloud, Inc.", - "license": "MIT", "dependencies": { "download": "^8.0.0", - "tencent-component-toolkit": "^1.11.4", + "tencent-component-toolkit": "^1.12.9", "type": "^2.0.0" } } diff --git a/src/serverless.js b/src/serverless.js index 52f114b..bad108d 100644 --- a/src/serverless.js +++ b/src/serverless.js @@ -51,6 +51,26 @@ class ServerlessComponent extends Component { ...(this.state[curRegion] ? this.state[curRegion] : {}), ...outputs[curRegion] } + + // default version is $LATEST + outputs[curRegion].lastVersion = scfOutput.LastVersion + ? scfOutput.LastVersion + : this.state.lastVersion || '$LATEST' + + // default traffic is 1.0, it can also be 0, so we should compare to undefined + outputs[curRegion].traffic = scfOutput.Traffic + ? scfOutput.Traffic + : this.state.traffic !== undefined + ? this.state.traffic + : 1 + + if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) { + outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion + this.state.configTrafficVersion = scfOutput.ConfigTrafficVersion + } + + this.state.lastVersion = outputs[curRegion].lastVersion + this.state.traffic = outputs[curRegion].traffic } uploadCodeHandler.push(funcDeployer()) } diff --git a/src/utils.js b/src/utils.js index 0bf0264..ff8212e 100644 --- a/src/utils.js +++ b/src/utils.js @@ -8,13 +8,6 @@ const download = require('download') const { TypeError } = require('tencent-component-toolkit/src/utils/error') const CONFIGS = require('./config') -/* - * Pauses execution for the provided miliseconds - * - * @param ${number} wait - number of miliseconds to wait - */ -const sleep = async (wait) => new Promise((resolve) => setTimeout(() => resolve(), wait)) - /* * Generates a random id */ @@ -23,18 +16,28 @@ const generateId = () => .toString(36) .substring(6) -const getDirFiles = async (dirPath) => { - const targetPath = path.resolve(dirPath) - const files = fs.readdirSync(targetPath) - const temp = {} - files.forEach((file) => { - temp[file] = path.join(targetPath, file) - }) - return temp +const getType = (obj) => { + return Object.prototype.toString.call(obj).slice(8, -1) +} + +const validateTraffic = (num) => { + if (getType(num) !== 'Number') { + throw new TypeError( + `PARAMETER_${CONFIGS.compName.toUpperCase()}_TRAFFIC`, + 'traffic must be a number' + ) + } + if (num < 0 || num > 1) { + throw new TypeError( + `PARAMETER_${CONFIGS.compName.toUpperCase()}_TRAFFIC`, + 'traffic must be a number between 0 and 1' + ) + } + return true } const getCodeZipPath = async (instance, inputs) => { - console.log(`Packaging ${CONFIGS.compNameFullname} application...`) + console.log(`Packaging ${CONFIGS.compFullname} application...`) // unzip source zip file let zipPath @@ -43,7 +46,7 @@ const getCodeZipPath = async (instance, inputs) => { const downloadPath = `/tmp/${generateId()}` const filename = 'template' - console.log(`Installing Default ${CONFIGS.compNameFullname} App...`) + console.log(`Installing Default ${CONFIGS.compFullname} App...`) try { await download(CONFIGS.templateUrl, downloadPath, { filename: `${filename}.zip` @@ -59,6 +62,16 @@ const getCodeZipPath = async (instance, inputs) => { return zipPath } +const getDirFiles = async (dirPath) => { + const targetPath = path.resolve(dirPath) + const files = fs.readdirSync(targetPath) + const temp = {} + files.forEach((file) => { + temp[file] = path.join(targetPath, file) + }) + return temp +} + /** * Upload code to COS * @param {Component} instance serverless component instance @@ -235,8 +248,18 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { fromClientRemark, layers: ensureIterable(tempFunctionConf.layers ? tempFunctionConf.layers : inputs.layers, { default: [] - }) + }), + publish: inputs.publish, + traffic: inputs.traffic, + lastVersion: instance.state.lastVersion } + + // validate traffic + if (inputs.traffic !== undefined) { + validateTraffic(inputs.traffic) + } + functionConf.needSetTraffic = inputs.traffic !== undefined && functionConf.lastVersion + functionConf.tags = ensureObject(tempFunctionConf.tags ? tempFunctionConf.tags : inputs.tag, { default: null }) @@ -267,7 +290,7 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { // 对apigw inputs进行标准化 const apigatewayConf = inputs.apigatewayConf ? inputs.apigatewayConf : {} - apigatewayConf.isDisabled = apigatewayConf.isDisabled === true + apigatewayConf.isDisabled = inputs.apigatewayConf === true apigatewayConf.fromClientRemark = fromClientRemark apigatewayConf.serviceName = inputs.serviceName apigatewayConf.description = `Serverless Framework Tencent-${capitalString( @@ -286,7 +309,8 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { function: { isIntegratedResponse: apigatewayConf.isIntegratedResponse === false ? false : true, functionName: functionConf.name, - functionNamespace: functionConf.namespace + functionNamespace: functionConf.namespace, + functionQualifier: functionConf.needSetTraffic ? '$DEFAULT' : '$LATEST' } } ] @@ -371,7 +395,6 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { module.exports = { generateId, - sleep, uploadCodeToCos, mergeJson, capitalString, From 305afcf6c5951c697f29e7090fecc3e2ecc5807b Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 6 Jul 2020 19:47:12 +0800 Subject: [PATCH 03/36] chore: release v0.0.2 --- example/app.py | 2 +- example/serverless.yml | 2 +- package.json | 2 +- serverless.component.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/example/app.py b/example/app.py index 82a0f6f..cd62520 100644 --- a/example/app.py +++ b/example/app.py @@ -4,7 +4,7 @@ @app.route("/") def index(): - return "Hello Flask" + return "Flask Restful API Created By Serverless Component" @app.route("/users") diff --git a/example/serverless.yml b/example/serverless.yml index b28ba97..18d6dc2 100644 --- a/example/serverless.yml +++ b/example/serverless.yml @@ -1,7 +1,7 @@ org: orgDemo # (optional) serverless dashboard org. default is the first org you created during signup. app: appDemo # (optional) serverless dashboard app. default is the same as the name property. stage: dev # (optional) serverless dashboard stage. default is dev. -component: flask@dev # (required) name of the component. In that case, it's flask. +component: flask # (required) name of the component. In that case, it's flask. name: flashDemo # (required) name of your flash component instance. inputs: diff --git a/package.json b/package.json index 1b07f3f..fca6d1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@serverless/flask", - "version": "0.0.1", + "version": "0.0.2", "main": "src/serverless.js", "publishConfig": { "access": "public" diff --git a/serverless.component.yml b/serverless.component.yml index 20d288f..c5b28af 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: flask -version: 0.0.1 +version: 0.0.2 author: Tencent Cloud, Inc org: Tencent Cloud, Inc description: Deploy a serverless Flask application onto Tencent SCF and APIGateway. From 363160a4741ded97a6baffe2def7bba21639a679 Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 7 Jul 2020 18:34:13 +0800 Subject: [PATCH 04/36] docs: update configure document --- docs/configure.md | 69 ++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index 7359fb4..d0c27e0 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -6,18 +6,18 @@ # serverless.yml component: flask # (必选) 组件名称,在该实例中为flask -name: flashDemo # (必选) 组件实例名称. +name: flaskDemo # 必选) 组件实例名称. org: orgDemo # (可选) 用于记录组织信息,默认值为您的腾讯云账户 appid,必须为字符串 app: appDemo # (可选) 用于记录组织信息. 默认与name相同,必须为字符串 stage: dev # (可选) 用于区分环境信息,默认值是 dev inputs: - src: # 打包src对应目录下的代码上传到默认cos上 - hook: 'pip install -r requirements.txt -t ./' # (可选) 安装python相关依赖,每次执行部署前都会构建代码并放到当前目录下 - dist: ./ # (可选) 要上传的源码的路径,默认为一个hello world app - exclude: # (可选) 被排除的文件或目录 - - .env - # src: ./src # 第一种为string时,会打包src对应目录下的代码上传到默认cos上。 + region: ap-guangzhou # 云函数所在区域 + functionName: flaskDemo # 云函数名称 + serviceName: mytest # api网关服务名称 + runtime: Nodejs10.15 # 运行环境 + serviceId: service-np1uloxw # api网关服务ID + src: ./src # 第一种为string时,会打包src对应目录下的代码上传到默认cos上。 # src: # 第二种,部署src下的文件代码,并打包成zip上传到bucket上 # src: ./src # 本地需要打包的文件目录 # bucket: bucket01 # bucket name,当前会默认在bucket name后增加 appid 后缀, 本例中为 bucket01-appid @@ -27,14 +27,10 @@ inputs: # src: # 第三种,在指定存储桶bucket中已经存在了object代码,直接部署 # bucket: bucket01 # bucket name,当前会默认在bucket name后增加 appid 后缀, 本例中为 bucket01-appid # object: cos.zip # bucket key 指定存储桶内的文件 - region: ap-guangzhou # 云函数所在区域 - functionName: expressDemo # 云函数名称 - serviceName: mytest # api网关服务名称 - runtime: Python3.6 # 运行环境 - serviceId: service-np1uloxw # api网关服务ID layers: - name: layerName # layer名称 version: 1 # 版本 + traffic: 0.9 # 配置默认流量中 $LATEST 版本比重:0 - 1 functionConf: # 函数配置相关 timeout: 10 # 超时时间,单位秒 memorySize: 128 # 内存大小,单位MB @@ -42,19 +38,19 @@ inputs: variables: # 环境变量数组 TEST: vale vpcConfig: # 私有网络配置 - subnetId: '' # 私有网络的Id - vpcId: '' # 子网ID + vpcId: '' # 私有网络的Id + subnetId: '' # 子网ID apigatewayConf: # api网关配置 isDisabled: false # 是否禁用自动创建 API 网关功能 enableCORS: true # 允许跨域 customDomains: # 自定义域名绑定 - domain: abc.com # 待绑定的自定义的域名 certificateId: abcdefg # 待绑定自定义域名的证书唯一 ID - # 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效 + # 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。 pathMappingSet: - path: / environment: release - protocols: # 绑定自定义域名的协议类型,默认与服务的前端协议一致 + protocols: # 绑定自定义域名的协议类型,默认与服务的前端协议一致。 - http # 支持http协议 - https # 支持https协议 protocols: @@ -77,30 +73,29 @@ inputs: 主要的参数 -| 参数名称 | 是否必选 | 默认值 | 描述 | -| ------------------------------------ | :------: | :-------------: | :------------------------------------------------------------- | -| runtime | 否 | Python3.6 | 执行环境, 目前支持: Python3.x | -| region | 否 | ap-guangzhou | 项目部署所在区域,默认广州区 | -| functionName | 否 | | 云函数名称 | -| serviceName | 否 | | API 网关服务名称, 默认创建一个新的服务名称 | -| serviceId | 否 | | API 网关服务 ID,如果存在将使用这个 API 网关服务 | -| src | 否 | `process.cwd()` | 默认为当前目录, 如果是对象, 配置参数参考 [执行目录](#执行目录) | -| layers | 否 | | 云函数绑定的 layer, 配置参数参考 [层配置](#层配置) | -| exclude | 否 | | 不包含的文件 | -| include | 否 | | 包含的文件, 如果是相对路径,是相对于 `serverless.yml`的路径 | -| [functionConf](#函数配置) | 否 | | 函数配置 | -| [apigatewayConf](#API-网关配置) | 否 | | API 网关配置 | -| [cloudDNSConf](#DNS-配置) | 否 | | DNS 配置 | -| [Region special config](#指定区配置) | 否 | | 指定区配置 | +| 参数名称 | 是否必选 | 默认值 | 描述 | +| ------------------------------------ | :------: | :-------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| runtime | 否 | Python3.6 | 执行环境, 目前支持: Python3.6, Python2.7 | +| region | 否 | ap-guangzhou | 项目部署所在区域,默认广州区 | +| functionName | 否 | | 云函数名称 | +| serviceName | 否 | | API 网关服务名称, 默认创建一个新的服务名称 | +| serviceId | 否 | | API 网关服务 ID,如果存在将使用这个 API 网关服务 | +| src | 否 | `process.cwd()` | 默认为当前目录, 如果是对象, 配置参数参考 [执行目录](#执行目录) | +| layers | 否 | | 云函数绑定的 layer, 配置参数参考 [层配置](#层配置) | +| traffic | 否 | 1 | 配置默认流量中 `$LATEST` 版本比重,取值范围:0 ~ 1,比如 80%,可配置成 0.8。注意如果配置灰度流量,需要配置对应的 API 网关触发器的 endpoints 的 `function.functionQualifier` 参数为 `$DEFAULT` (默认流量) | +| [functionConf](#函数配置) | 否 | | 函数配置 | +| [apigatewayConf](#API-网关配置) | 否 | | API 网关配置 | +| [cloudDNSConf](#DNS-配置) | 否 | | DNS 配置 | +| [Region special config](#指定区配置) | 否 | | 指定区配置 | ## 执行目录 -| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | -| -------- | -------- | --------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| src | 否 | String | | 代码路径。与 object 不能同时存在。 | -| exclude | 否 | Array of String | | 不包含的文件或路径, 遵守 [glob 语法](https://github.com/isaacs/node-glob) | -| bucket | 否 | String | | bucket 名称。如果配置了 src,表示部署 src 的代码并压缩成 zip 后上传到 bucket-appid 对应的存储桶中;如果配置了 object,表示获取 bucket-appid 对应存储桶中 object 对应的代码进行部署。 | -| object | 否 | String | | 部署的代码在存储桶中的路径。 | | +| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | +| -------- | :------: | :-------------: | :----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| src | 否 | String | | 代码路径。与 object 不能同时存在。 | +| exclude | 否 | Array of String | | 不包含的文件或路径, 遵守 [glob 语法](https://github.com/isaacs/node-glob) | +| bucket | 否 | String | | bucket 名称。如果配置了 src,表示部署 src 的代码并压缩成 zip 后上传到 bucket-appid 对应的存储桶中;如果配置了 object,表示获取 bucket-appid 对应存储桶中 object 对应的代码进行部署。 | +| object | 否 | String | | 部署的代码在存储桶中的路径。 | ## 层配置 From 55fdd0f5db9069aa37d10371626cd98c879a9212 Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 7 Jul 2020 18:34:39 +0800 Subject: [PATCH 05/36] ci: disable auto release --- .travis.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index b9f070c..f1ee195 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,12 +11,12 @@ install: jobs: include: # Define the release stage that runs semantic-release - - stage: release - node_js: 10.18 - # Advanced: optionally overwrite your default `script` step to skip the tests - # script: skip - deploy: - provider: script - skip_cleanup: true - script: - - npm run release + # - stage: release + # node_js: 10.18 + # # Advanced: optionally overwrite your default `script` step to skip the tests + # # script: skip + # deploy: + # provider: script + # skip_cleanup: true + # script: + # - npm run release From adfd47af9670fc23ed46d9a068c2a1396f25e96f Mon Sep 17 00:00:00 2001 From: yugasun Date: Wed, 8 Jul 2020 15:20:59 +0800 Subject: [PATCH 06/36] chore: update example yaml config --- example/.gitignore | 1 + example/serverless.yml | 18 +++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 example/.gitignore diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..2cef6cd --- /dev/null +++ b/example/.gitignore @@ -0,0 +1 @@ +requirements diff --git a/example/serverless.yml b/example/serverless.yml index 18d6dc2..0b423f4 100644 --- a/example/serverless.yml +++ b/example/serverless.yml @@ -1,16 +1,20 @@ -org: orgDemo # (optional) serverless dashboard org. default is the first org you created during signup. -app: appDemo # (optional) serverless dashboard app. default is the same as the name property. -stage: dev # (optional) serverless dashboard stage. default is dev. -component: flask # (required) name of the component. In that case, it's flask. -name: flashDemo # (required) name of your flash component instance. +org: orgDemo +app: appDemo +stage: dev +component: flask +name: flaskDemo inputs: src: # TODO: 安装python项目依赖到项目当前目录 - hook: 'pip3 install -r requirements.txt -t ./' - dist: ./ # (optional) path to the source folder. default is a hello world app. + hook: 'pip3 install -r requirements.txt -t ./requirements' + dist: ./ + include: + - source: ./requirements + prefix: ../ # prefix, can make ./requirements files/dir to ./ exclude: - .env + - 'requirements/**' region: ap-guangzhou runtime: Python3.6 apigatewayConf: From 7a6aead877bfe58a0168c6ff9628ec250c292cee Mon Sep 17 00:00:00 2001 From: yugasun Date: Thu, 9 Jul 2020 20:58:03 +0800 Subject: [PATCH 07/36] fix: upgrade deps --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 157a43e..1c0cd56 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "dependencies": { "download": "^8.0.0", - "tencent-component-toolkit": "^1.12.9", + "tencent-component-toolkit": "^1.12.10", "type": "^2.0.0" } } From 092ce16d5287ec08dc30edc6e0f28d25512dc32f Mon Sep 17 00:00:00 2001 From: yugasun Date: Thu, 16 Jul 2020 19:57:35 +0800 Subject: [PATCH 08/36] chore: set webDeployable true --- serverless.component.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/serverless.component.yml b/serverless.component.yml index c5b28af..49afec1 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -8,3 +8,4 @@ repo: https://github.com/serverless-components/tencent-flask readme: https://github.com/serverless-components/tencent-flask/tree/master/README.md license: MIT main: ./src +webDeployable: true From b0366d77d78c754eecf0d06c1ec2d7aa6197ff58 Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 21 Jul 2020 19:18:43 +0800 Subject: [PATCH 09/36] fix: upgrade deps --- serverless.component.yml | 2 +- src/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/serverless.component.yml b/serverless.component.yml index 49afec1..cc69f53 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -2,7 +2,7 @@ name: flask version: 0.0.2 author: Tencent Cloud, Inc org: Tencent Cloud, Inc -description: Deploy a serverless Flask application onto Tencent SCF and APIGateway. +description: Deploy a serverless Flask application onto Tencent SCF and API Gateway. keywords: tencent, serverless, flask repo: https://github.com/serverless-components/tencent-flask readme: https://github.com/serverless-components/tencent-flask/tree/master/README.md diff --git a/src/package.json b/src/package.json index 1c0cd56..b0203fe 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "dependencies": { "download": "^8.0.0", - "tencent-component-toolkit": "^1.12.10", + "tencent-component-toolkit": "^1.12.15", "type": "^2.0.0" } } From 6b94798c381bb720f53679a14b31a28ba2d565f0 Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 21 Jul 2020 19:22:09 +0800 Subject: [PATCH 10/36] chore: release v0.0.3 --- package.json | 2 +- serverless.component.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fca6d1b..92dbdaf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@serverless/flask", - "version": "0.0.2", + "version": "0.0.3", "main": "src/serverless.js", "publishConfig": { "access": "public" diff --git a/serverless.component.yml b/serverless.component.yml index cc69f53..947ee06 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: flask -version: 0.0.2 +version: 0.0.3 author: Tencent Cloud, Inc org: Tencent Cloud, Inc description: Deploy a serverless Flask application onto Tencent SCF and API Gateway. From 7b033f6d33ea2b1322e69a51d28c3bd1ce25189f Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 21 Jul 2020 20:10:48 +0800 Subject: [PATCH 11/36] docs: update configure --- docs/configure.md | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index d0c27e0..785a216 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -30,7 +30,6 @@ inputs: layers: - name: layerName # layer名称 version: 1 # 版本 - traffic: 0.9 # 配置默认流量中 $LATEST 版本比重:0 - 1 functionConf: # 函数配置相关 timeout: 10 # 超时时间,单位秒 memorySize: 128 # 内存大小,单位MB @@ -73,20 +72,19 @@ inputs: 主要的参数 -| 参数名称 | 是否必选 | 默认值 | 描述 | -| ------------------------------------ | :------: | :-------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| runtime | 否 | Python3.6 | 执行环境, 目前支持: Python3.6, Python2.7 | -| region | 否 | ap-guangzhou | 项目部署所在区域,默认广州区 | -| functionName | 否 | | 云函数名称 | -| serviceName | 否 | | API 网关服务名称, 默认创建一个新的服务名称 | -| serviceId | 否 | | API 网关服务 ID,如果存在将使用这个 API 网关服务 | -| src | 否 | `process.cwd()` | 默认为当前目录, 如果是对象, 配置参数参考 [执行目录](#执行目录) | -| layers | 否 | | 云函数绑定的 layer, 配置参数参考 [层配置](#层配置) | -| traffic | 否 | 1 | 配置默认流量中 `$LATEST` 版本比重,取值范围:0 ~ 1,比如 80%,可配置成 0.8。注意如果配置灰度流量,需要配置对应的 API 网关触发器的 endpoints 的 `function.functionQualifier` 参数为 `$DEFAULT` (默认流量) | -| [functionConf](#函数配置) | 否 | | 函数配置 | -| [apigatewayConf](#API-网关配置) | 否 | | API 网关配置 | -| [cloudDNSConf](#DNS-配置) | 否 | | DNS 配置 | -| [Region special config](#指定区配置) | 否 | | 指定区配置 | +| 参数名称 | 是否必选 | 默认值 | 描述 | +| ------------------------------------ | :------: | :-------------: | :------------------------------------------------------------- | +| runtime | 否 | Python3.6 | 执行环境, 目前支持: Python3.6, Python2.7 | +| region | 否 | ap-guangzhou | 项目部署所在区域,默认广州区 | +| functionName | 否 | | 云函数名称 | +| serviceName | 否 | | API 网关服务名称, 默认创建一个新的服务名称 | +| serviceId | 否 | | API 网关服务 ID,如果存在将使用这个 API 网关服务 | +| src | 否 | `process.cwd()` | 默认为当前目录, 如果是对象, 配置参数参考 [执行目录](#执行目录) | +| layers | 否 | | 云函数绑定的 layer, 配置参数参考 [层配置](#层配置) | +| [functionConf](#函数配置) | 否 | | 函数配置 | +| [apigatewayConf](#API-网关配置) | 否 | | API 网关配置 | +| [cloudDNSConf](#DNS-配置) | 否 | | DNS 配置 | +| [Region special config](#指定区配置) | 否 | | 指定区配置 | ## 执行目录 From 3ede243da630a0a9147b60242e42032bb53b4418 Mon Sep 17 00:00:00 2001 From: yugasun Date: Wed, 22 Jul 2020 16:14:23 +0800 Subject: [PATCH 12/36] docs: update configure isDefaultMapping --- docs/configure.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index 785a216..b2912f5 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -45,6 +45,8 @@ inputs: customDomains: # 自定义域名绑定 - domain: abc.com # 待绑定的自定义的域名 certificateId: abcdefg # 待绑定自定义域名的证书唯一 ID + # 如要设置自定义路径映射,请设置为 false + isDefaultMapping: false # 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。 pathMappingSet: - path: / @@ -180,13 +182,13 @@ inputs: Refer to: https://cloud.tencent.com/document/product/628/14906 -| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | -| ---------------- | :------: | :------: | :------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| domain | 是 | String | | 待绑定的自定义的域名。 | -| certificateId | 否 | String | | 待绑定自定义域名的证书唯一 ID,如果设置了 type 为 https,则为必选 | -| isDefaultMapping | 否 | String | `'TRUE'` | 是否使用默认路径映射,默认为 TRUE。为 FALSE 时,表示自定义路径映射,此时 pathMappingSet 必填。 | -| pathMappingSet | 否 | Object[] | `[]` | 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。 | -| protocol | 否 | String[] | | 绑定自定义域名的协议类型,默认与服务的前端协议一致。 | +| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | +| ---------------- | :------: | :------: | :----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| domain | 是 | String | | 待绑定的自定义的域名。 | +| certificateId | 否 | String | | 待绑定自定义域名的证书唯一 ID,如果设置了 type 为 https,则为必选 | +| isDefaultMapping | 否 | String | `true` | 是否使用默认路径映射,默认为 true。为 false 时,表示自定义路径映射,此时 pathMappingSet 必填。 | +| pathMappingSet | 否 | Object[] | `[]` | 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。 | +| protocol | 否 | String[] | | 绑定自定义域名的协议类型,默认与服务的前端协议一致。 | - 自定义路径映射 From 4dfde9610102d0cd7b081b42b329474df8513378 Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 27 Jul 2020 16:46:52 +0800 Subject: [PATCH 13/36] fix: upgrade deps --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index b0203fe..9e5637b 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "dependencies": { "download": "^8.0.0", - "tencent-component-toolkit": "^1.12.15", + "tencent-component-toolkit": "^1.13.0", "type": "^2.0.0" } } From e5eaa6a4a55982a5918ed0371115584334db0fac Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 27 Jul 2020 16:48:30 +0800 Subject: [PATCH 14/36] chore: release v0.0.4 --- package.json | 2 +- serverless.component.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 92dbdaf..0ea4204 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@serverless/flask", - "version": "0.0.3", + "version": "0.0.4", "main": "src/serverless.js", "publishConfig": { "access": "public" diff --git a/serverless.component.yml b/serverless.component.yml index 947ee06..b7b5efc 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: flask -version: 0.0.3 +version: 0.0.4 author: Tencent Cloud, Inc org: Tencent Cloud, Inc description: Deploy a serverless Flask application onto Tencent SCF and API Gateway. From aaf8ca1dc166e37f9a635695f0dc2c52ff9f3243 Mon Sep 17 00:00:00 2001 From: yugasun Date: Thu, 6 Aug 2020 18:21:56 +0800 Subject: [PATCH 15/36] fix: support eip config --- docs/configure.md | 42 ++++++++++++++++++++++-------------------- src/config.js | 1 - src/package.json | 2 +- src/utils.js | 13 +++---------- 4 files changed, 26 insertions(+), 32 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index b2912f5..8d00c44 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -32,6 +32,7 @@ inputs: version: 1 # 版本 functionConf: # 函数配置相关 timeout: 10 # 超时时间,单位秒 + eip: false # 是否固定出口IP memorySize: 128 # 内存大小,单位MB environment: # 环境变量 variables: # 环境变量数组 @@ -76,8 +77,8 @@ inputs: | 参数名称 | 是否必选 | 默认值 | 描述 | | ------------------------------------ | :------: | :-------------: | :------------------------------------------------------------- | -| runtime | 否 | Python3.6 | 执行环境, 目前支持: Python3.6, Python2.7 | -| region | 否 | ap-guangzhou | 项目部署所在区域,默认广州区 | +| runtime | 否 | `Python3.6` | 执行环境, 目前支持: Python3.6, Python2.7 | +| region | 否 | `ap-guangzhou` | 项目部署所在区域,默认广州区 | | functionName | 否 | | 云函数名称 | | serviceName | 否 | | API 网关服务名称, 默认创建一个新的服务名称 | | serviceId | 否 | | API 网关服务 ID,如果存在将使用这个 API 网关服务 | @@ -110,7 +111,7 @@ inputs: | 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | | ---------- | :------: | -------- | :----: | :---------------------------------------------- | -| ttl | 否 | Number | 600 | TTL 值,范围 1 - 604800,不同等级域名最小值不同 | +| ttl | 否 | Number | `600` | TTL 值,范围 1 - 604800,不同等级域名最小值不同 | | recordLine | 否 | String[] | | 记录的线路名称 | ### 指定区配置 @@ -125,12 +126,13 @@ inputs: 参考: https://cloud.tencent.com/document/product/583/18586 -| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | -| ----------- | :------: | :----: | :----: | :------------------------------------------------------------------------------ | -| timeout | 否 | Number | 3 | 函数最长执行时间,单位为秒,可选值范围 1-900 秒,默认为 3 秒 | -| memorySize | 否 | Number | 128 | 函数运行时内存大小,默认为 128M,可选范围 64、128MB-3072MB,并且以 128MB 为阶梯 | -| environment | 否 | Object | | 函数的环境变量, 参考 [环境变量](#环境变量) | -| vpcConfig | 否 | Object | | 函数的 VPC 配置, 参考 [VPC 配置](#VPC-配置) | +| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | +| ----------- | :------: | :-----: | :-----: | :------------------------------------------------------------------------------ | +| timeout | 否 | Number | `3` | 函数最长执行时间,单位为秒,可选值范围 1-900 秒,默认为 3 秒 | +| memorySize | 否 | Number | `128` | 函数运行时内存大小,默认为 128M,可选范围 64、128MB-3072MB,并且以 128MB 为阶梯 | +| environment | 否 | Object | | 函数的环境变量, 参考 [环境变量](#环境变量) | +| vpcConfig | 否 | Object | | 函数的 VPC 配置, 参考 [VPC 配置](#VPC-配置) | +| eip | 否 | Boolean | `false` | 是否固定出口 IP | ##### 环境变量 @@ -147,16 +149,16 @@ inputs: ### API 网关配置 -| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | -| -------------- | :------: | :------- | :------- | :--------------------------------------------------------------------------------- | -| protocols | 否 | String[] | ['http'] | 前端请求的类型,如 http,https,http 与 https | -| environment | 否 | String | release | 发布环境. 目前支持三种发布环境: test(测试), prepub(预发布) 与 release(发布). | -| usagePlan | 否 | | | 使用计划配置, 参考 [使用计划](#使用计划) | -| auth | 否 | | | API 密钥配置, 参考 [API 密钥](#API-密钥配置) | -| customDomain | 否 | Object[] | | 自定义 API 域名配置, 参考 [自定义域名](#自定义域名) | -| enableCORS | 否 | Boolean | `false` | 开启跨域。默认值为否。 | -| serviceTimeout | 否 | Number | `15` | Api 超时时间,单位: 秒 | -| isDisabled | 否 | Boolean | `false` | 关闭自动创建 API 网关功能。默认值为否,即默认自动创建 API 网关。 | +| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | +| -------------- | :------: | :------- | :--------- | :--------------------------------------------------------------------------------- | +| protocols | 否 | String[] | `['http']` | 前端请求的类型,如 http,https,http 与 https | +| environment | 否 | String | `release` | 发布环境. 目前支持三种发布环境: test(测试), prepub(预发布) 与 release(发布). | +| usagePlan | 否 | | | 使用计划配置, 参考 [使用计划](#使用计划) | +| auth | 否 | | | API 密钥配置, 参考 [API 密钥](#API-密钥配置) | +| customDomain | 否 | Object[] | | 自定义 API 域名配置, 参考 [自定义域名](#自定义域名) | +| enableCORS | 否 | Boolean | `false` | 开启跨域。默认值为否。 | +| serviceTimeout | 否 | Number | `15` | Api 超时时间,单位: 秒 | +| isDisabled | 否 | Boolean | `false` | 关闭自动创建 API 网关功能。默认值为否,即默认自动创建 API 网关。 | ##### 使用计划 @@ -186,7 +188,7 @@ Refer to: https://cloud.tencent.com/document/product/628/14906 | ---------------- | :------: | :------: | :----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | domain | 是 | String | | 待绑定的自定义的域名。 | | certificateId | 否 | String | | 待绑定自定义域名的证书唯一 ID,如果设置了 type 为 https,则为必选 | -| isDefaultMapping | 否 | String | `true` | 是否使用默认路径映射,默认为 true。为 false 时,表示自定义路径映射,此时 pathMappingSet 必填。 | +| isDefaultMapping | 否 | String | `true` | 是否使用默认路径映射。为 false 时,表示自定义路径映射,此时 pathMappingSet 必填。 | | pathMappingSet | 否 | Object[] | `[]` | 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。 | | protocol | 否 | String[] | | 绑定自定义域名的协议类型,默认与服务的前端协议一致。 | diff --git a/src/config.js b/src/config.js index d92282e..8116d62 100644 --- a/src/config.js +++ b/src/config.js @@ -4,7 +4,6 @@ const CONFIGS = { compFullname: 'Flask', handler: 'sl_handler.handler', runtime: 'Python3.6', - exclude: ['.git/**', '.gitignore', '.DS_Store'], timeout: 3, memorySize: 128, namespace: 'default', diff --git a/src/package.json b/src/package.json index 9e5637b..9a9d323 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "dependencies": { "download": "^8.0.0", - "tencent-component-toolkit": "^1.13.0", + "tencent-component-toolkit": "^1.13.2", "type": "^2.0.0" } } diff --git a/src/utils.js b/src/utils.js index ff8212e..026fa0f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -251,7 +251,9 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { }), publish: inputs.publish, traffic: inputs.traffic, - lastVersion: instance.state.lastVersion + lastVersion: instance.state.lastVersion, + eip: tempFunctionConf.eip === true, + l5Enable: tempFunctionConf.l5Enable === true } // validate traffic @@ -264,15 +266,6 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { default: null }) - functionConf.include = ensureIterable( - tempFunctionConf.include ? tempFunctionConf.include : inputs.include, - { default: [], ensureItem: ensureString } - ) - functionConf.exclude = ensureIterable( - tempFunctionConf.exclude ? tempFunctionConf.exclude : inputs.exclude, - { default: [], ensureItem: ensureString } - ) - functionConf.exclude.push('.git/**', '.gitignore', '.serverless', '.DS_Store') if (inputs.functionConf) { functionConf.timeout = inputs.functionConf.timeout ? inputs.functionConf.timeout From ffdbdf1a3b173db8e4919b01cc9f36862e3e086b Mon Sep 17 00:00:00 2001 From: yugasun Date: Thu, 6 Aug 2020 18:27:42 +0800 Subject: [PATCH 16/36] chore: release v0.0.5 --- package.json | 2 +- serverless.component.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0ea4204..76bc5fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@serverless/flask", - "version": "0.0.4", + "version": "0.0.5", "main": "src/serverless.js", "publishConfig": { "access": "public" diff --git a/serverless.component.yml b/serverless.component.yml index b7b5efc..fc7d7ae 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: flask -version: 0.0.4 +version: 0.0.5 author: Tencent Cloud, Inc org: Tencent Cloud, Inc description: Deploy a serverless Flask application onto Tencent SCF and API Gateway. From d80d4218bbd51c51cd4a590efc6cffe4fce6f959 Mon Sep 17 00:00:00 2001 From: yugasun Date: Wed, 19 Aug 2020 16:35:31 +0800 Subject: [PATCH 17/36] fix: traffic zero display bug --- src/serverless.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/serverless.js b/src/serverless.js index bad108d..e961608 100644 --- a/src/serverless.js +++ b/src/serverless.js @@ -58,11 +58,12 @@ class ServerlessComponent extends Component { : this.state.lastVersion || '$LATEST' // default traffic is 1.0, it can also be 0, so we should compare to undefined - outputs[curRegion].traffic = scfOutput.Traffic - ? scfOutput.Traffic - : this.state.traffic !== undefined - ? this.state.traffic - : 1 + outputs[curRegion].traffic = + scfOutput.Traffic !== undefined + ? scfOutput.Traffic + : this.state.traffic !== undefined + ? this.state.traffic + : 1 if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) { outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion From 5702fa9651a563866a2a8ed351993e3cb0bf9b61 Mon Sep 17 00:00:00 2001 From: Avril Li Date: Wed, 26 Aug 2020 19:38:05 +0800 Subject: [PATCH 18/36] chore: enable semantic release --- .prettierignore | 2 ++ .travis.yml | 21 +++++++++++---------- release.config.js | 12 ++---------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/.prettierignore b/.prettierignore index 18f2b36..ed450ca 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,5 @@ coverage dist node_modules +CHANGELOG.md +*.test.js \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index f1ee195..d78fad9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: node_js node_js: - - 8 - 10 install: @@ -11,12 +10,14 @@ install: jobs: include: # Define the release stage that runs semantic-release - # - stage: release - # node_js: 10.18 - # # Advanced: optionally overwrite your default `script` step to skip the tests - # # script: skip - # deploy: - # provider: script - # skip_cleanup: true - # script: - # - npm run release + - stage: release + node_js: 10.18 + # Advanced: optionally overwrite your default `script` step to skip the tests + # script: skip + deploy: + provider: script + skip_cleanup: true + on: + branch: master + script: + - npm run release diff --git a/release.config.js b/release.config.js index 53f3398..a13798c 100644 --- a/release.config.js +++ b/release.config.js @@ -1,7 +1,6 @@ module.exports = { verifyConditions: [ '@semantic-release/changelog', - '@semantic-release/npm', '@semantic-release/git', '@semantic-release/github' ], @@ -12,7 +11,8 @@ module.exports = { preset: 'angular', parserOpts: { noteKeywords: ['BREAKING CHANGE', 'BREAKING CHANGES', 'BREAKING'] - } + }, + releaseRules: [{ type: 'feat', release: 'patch' }] } ], [ @@ -33,14 +33,6 @@ module.exports = { changelogFile: 'CHANGELOG.md' } ], - [ - '@semantic-release/npm', - { - pkgRoot: '.', - npmPublish: false, - tarballDir: false - } - ], [ '@semantic-release/git', { From 63a89fafeb013297a1b00d0a553ba0968d88eaa6 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 26 Aug 2020 11:44:01 +0000 Subject: [PATCH 19/36] chore(release): version 0.0.5 ## [0.0.5](https://github.com/serverless-components/tencent-flask/compare/v0.0.4...v0.0.5) (2020-08-26) ### Bug Fixes * deploy error ([5934744](https://github.com/serverless-components/tencent-flask/commit/59347449c62fec0784a06d373a64a9635786108a)) * prettier config ([46a7011](https://github.com/serverless-components/tencent-flask/commit/46a701142c21b7fd5a069599a0c429a3942b1e38)) * support eip config ([aaf8ca1](https://github.com/serverless-components/tencent-flask/commit/aaf8ca1dc166e37f9a635695f0dc2c52ff9f3243)) * traffic zero display bug ([d80d421](https://github.com/serverless-components/tencent-flask/commit/d80d4218bbd51c51cd4a590efc6cffe4fce6f959)) * update get credential error message ([3964862](https://github.com/serverless-components/tencent-flask/commit/396486273ead2dcacec85607418bc4f06db95ea2)) * upgrade deps ([4dfde96](https://github.com/serverless-components/tencent-flask/commit/4dfde9610102d0cd7b081b42b329474df8513378)) * upgrade deps ([b0366d7](https://github.com/serverless-components/tencent-flask/commit/b0366d77d78c754eecf0d06c1ec2d7aa6197ff58)) * upgrade deps ([7a6aead](https://github.com/serverless-components/tencent-flask/commit/7a6aead877bfe58a0168c6ff9628ec250c292cee)) ### Features * init v2 ([245ce3a](https://github.com/serverless-components/tencent-flask/commit/245ce3a09e36e3224ead0381c97ab7d684d67903)) * support scf publish version and traffic setup ([661bd44](https://github.com/serverless-components/tencent-flask/commit/661bd449a7c51801163f537e4ea12837542f119b)) * support usage plan & auth ([f8084f5](https://github.com/serverless-components/tencent-flask/commit/f8084f5fa3d506ddc9f8e37fb8b53a0afd6183ad)) * update config & docs ([cb3f8d4](https://github.com/serverless-components/tencent-flask/commit/cb3f8d4c939041cfcec09a62d371453e5c4ec9f5)) --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..01aa130 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,21 @@ +## [0.0.5](https://github.com/serverless-components/tencent-flask/compare/v0.0.4...v0.0.5) (2020-08-26) + + +### Bug Fixes + +* deploy error ([5934744](https://github.com/serverless-components/tencent-flask/commit/59347449c62fec0784a06d373a64a9635786108a)) +* prettier config ([46a7011](https://github.com/serverless-components/tencent-flask/commit/46a701142c21b7fd5a069599a0c429a3942b1e38)) +* support eip config ([aaf8ca1](https://github.com/serverless-components/tencent-flask/commit/aaf8ca1dc166e37f9a635695f0dc2c52ff9f3243)) +* traffic zero display bug ([d80d421](https://github.com/serverless-components/tencent-flask/commit/d80d4218bbd51c51cd4a590efc6cffe4fce6f959)) +* update get credential error message ([3964862](https://github.com/serverless-components/tencent-flask/commit/396486273ead2dcacec85607418bc4f06db95ea2)) +* upgrade deps ([4dfde96](https://github.com/serverless-components/tencent-flask/commit/4dfde9610102d0cd7b081b42b329474df8513378)) +* upgrade deps ([b0366d7](https://github.com/serverless-components/tencent-flask/commit/b0366d77d78c754eecf0d06c1ec2d7aa6197ff58)) +* upgrade deps ([7a6aead](https://github.com/serverless-components/tencent-flask/commit/7a6aead877bfe58a0168c6ff9628ec250c292cee)) + + +### Features + +* init v2 ([245ce3a](https://github.com/serverless-components/tencent-flask/commit/245ce3a09e36e3224ead0381c97ab7d684d67903)) +* support scf publish version and traffic setup ([661bd44](https://github.com/serverless-components/tencent-flask/commit/661bd449a7c51801163f537e4ea12837542f119b)) +* support usage plan & auth ([f8084f5](https://github.com/serverless-components/tencent-flask/commit/f8084f5fa3d506ddc9f8e37fb8b53a0afd6183ad)) +* update config & docs ([cb3f8d4](https://github.com/serverless-components/tencent-flask/commit/cb3f8d4c939041cfcec09a62d371453e5c4ec9f5)) From 40b99c12af75d87ba5d83cda0e44c383f8dd7d27 Mon Sep 17 00:00:00 2001 From: Avril Li Date: Wed, 26 Aug 2020 19:58:02 +0800 Subject: [PATCH 20/36] chore: update release rule --- package.json | 1 - release.config.js | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index 76bc5fc..3d9a1e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,5 @@ { "name": "@serverless/flask", - "version": "0.0.5", "main": "src/serverless.js", "publishConfig": { "access": "public" diff --git a/release.config.js b/release.config.js index a13798c..98b3864 100644 --- a/release.config.js +++ b/release.config.js @@ -11,8 +11,7 @@ module.exports = { preset: 'angular', parserOpts: { noteKeywords: ['BREAKING CHANGE', 'BREAKING CHANGES', 'BREAKING'] - }, - releaseRules: [{ type: 'feat', release: 'patch' }] + } } ], [ From 27f437462b664930fd0483119d414705b660071b Mon Sep 17 00:00:00 2001 From: yugasun Date: Thu, 27 Aug 2020 16:04:51 +0800 Subject: [PATCH 21/36] fix: support cfs config --- serverless.component.yml | 2 +- src/package.json | 2 +- src/utils.js | 3 +++ tests/integration.test.js | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/serverless.component.yml b/serverless.component.yml index fc7d7ae..64f32f8 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: flask -version: 0.0.5 +version: 0.0.6 author: Tencent Cloud, Inc org: Tencent Cloud, Inc description: Deploy a serverless Flask application onto Tencent SCF and API Gateway. diff --git a/src/package.json b/src/package.json index 9a9d323..a53fc4f 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "dependencies": { "download": "^8.0.0", - "tencent-component-toolkit": "^1.13.2", + "tencent-component-toolkit": "^1.15.7", "type": "^2.0.0" } } diff --git a/src/utils.js b/src/utils.js index 026fa0f..c613450 100644 --- a/src/utils.js +++ b/src/utils.js @@ -249,6 +249,9 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { layers: ensureIterable(tempFunctionConf.layers ? tempFunctionConf.layers : inputs.layers, { default: [] }), + cfs: ensureIterable(tempFunctionConf.cfs ? tempFunctionConf.cfs : inputs.cfs, { + default: [] + }), publish: inputs.publish, traffic: inputs.traffic, lastVersion: instance.state.lastVersion, diff --git a/tests/integration.test.js b/tests/integration.test.js index 4d35f2b..5c6215a 100644 --- a/tests/integration.test.js +++ b/tests/integration.test.js @@ -7,7 +7,7 @@ jest.setTimeout(300000) const instanceYaml = { org: 'orgDemo', app: 'appDemo', - component: 'flask@dev', + component: 'flask', name: `flask-integration-tests-${generateId()}`, stage: 'dev', inputs: { From 99aa1111fb85d2888e5ae09caac45a5ba84570aa Mon Sep 17 00:00:00 2001 From: Avril Li Date: Wed, 2 Sep 2020 11:12:09 +0800 Subject: [PATCH 22/36] chore: add ygsec check --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 3d9a1e8..6369aff 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,9 @@ }, "husky": { "hooks": { - "pre-commit": "lint-staged", + "pre-commit": "ygsec && lint-staged", "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", - "pre-push": "npm run lint:fix && npm run prettier:fix" + "pre-push": "ygsec && npm run lint:fix && npm run prettier:fix" } }, "lint-staged": { @@ -45,13 +45,14 @@ "@semantic-release/npm": "^7.0.4", "@semantic-release/release-notes-generator": "^9.0.1", "@serverless/platform-client-china": "^1.0.19", + "@ygkit/secure": "0.0.3", "babel-eslint": "^10.1.0", "dotenv": "^8.2.0", "eslint": "^6.8.0", "eslint-config-prettier": "^6.10.0", "eslint-plugin-import": "^2.20.1", "eslint-plugin-prettier": "^3.1.2", - "husky": "^4.2.3", + "husky": "^4.2.5", "jest": "^25.0.1", "lint-staged": "^10.0.8", "prettier": "^1.19.1", From 96be3ff58daaaec45c4520ee9a8b7ce2b32bac1b Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 2 Sep 2020 03:13:55 +0000 Subject: [PATCH 23/36] chore(release): version 0.0.6 ## [0.0.6](https://github.com/serverless-components/tencent-flask/compare/v0.0.5...v0.0.6) (2020-09-02) ### Bug Fixes * support cfs config ([27f4374](https://github.com/serverless-components/tencent-flask/commit/27f437462b664930fd0483119d414705b660071b)) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01aa130..6353356 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.0.6](https://github.com/serverless-components/tencent-flask/compare/v0.0.5...v0.0.6) (2020-09-02) + + +### Bug Fixes + +* support cfs config ([27f4374](https://github.com/serverless-components/tencent-flask/commit/27f437462b664930fd0483119d414705b660071b)) + ## [0.0.5](https://github.com/serverless-components/tencent-flask/compare/v0.0.4...v0.0.5) (2020-08-26) From 86e5a498820c8f0312405593033fa9b0590f1478 Mon Sep 17 00:00:00 2001 From: yugasun Date: Wed, 2 Sep 2020 16:31:47 +0800 Subject: [PATCH 24/36] fix: update tencnet-component-toolkit for api mark --- README.en.md | 120 --------------------------------------- README.md | 20 ++++--- serverless.component.yml | 2 +- src/package.json | 2 +- 4 files changed, 15 insertions(+), 129 deletions(-) delete mode 100755 README.en.md diff --git a/README.en.md b/README.en.md deleted file mode 100755 index 5e92dc1..0000000 --- a/README.en.md +++ /dev/null @@ -1,120 +0,0 @@ -[![Serverless Python Flask Tencent Cloud](https://img.serverlesscloud.cn/20191226/1577347052683-flask_%E9%95%BF.png)](http://serverless.com) - -# Tencent Flask Serverless Component - -[简体中文](./README.md) | English - -## Introduction - -Tencent [Flask](https://github.com/pallets/flask) Serverless Component, support Restful API deploy, not supportting Flask command. - -## Content - -1. [Prepare](#0-prepare) -1. [Install](#1-install) -1. [Create](#2-create) -1. [Configure](#3-configure) -1. [Deploy](#4-deploy) -1. [Remove](#5-Remove) - -### 0. Prepare - -Before using this component, you need create a flask project, then add `Flask` and `werkzeug` in `requirements.txt`. Like below: - -```txt -Flask==1.0.2 -werkzeug==0.16.0 -``` - -Then create your API service entry file `app.py`, below is a example: - -```python -from flask import Flask, jsonify -app = Flask(__name__) - -@app.route("/") -def index(): - return "Hello Flash" - -@app.route("/users") -def users(): - users = [{'name': 'test1'}, {'name': 'test2'}] - return jsonify(data=users) - -@app.route("/users/") -def user(id): - return jsonify(data={'name': 'test1'}) -``` - -### 1. Install - -Install the Serverless Framework globally: - -```shell -$ npm install -g serverless -``` - -### 2. Create - -Just create the following simple boilerplate: - -```shell -$ touch serverless.yml -$ touch .env # your Tencent api keys -``` - -Add the access keys of a [Tencent CAM Role](https://console.cloud.tencent.com/cam/capi) with `AdministratorAccess` in the `.env` file, using this format: - -``` -# .env -TENCENT_SECRET_ID=XXX -TENCENT_SECRET_KEY=XXX -``` - -- If you don't have a Tencent Cloud account, you could [sign up](https://intl.cloud.tencent.com/register) first. - -### 3. Configure - -```yml -# serverless.yml - -component: flask -name: flashDemo -org: orgDemo -app: appDemo -stage: dev - -inputs: - src: - hook: 'pip install -r requirements.txt -t ./' - dist: ./ - exclude: - - .env - region: ap-guangzhou - runtime: Python3.6 - apigatewayConf: - protocols: - - http - - https - environment: release -``` - -- [More Options](./docs/configure.md)) - -### 4. Deploy - -```shell -$ sls --debug -``` - -  - -### 5. Remove - -```shell -$ sls remove --debug -``` - -### More Components - -Checkout the [Serverless Components](https://github.com/serverless/components) repo for more information. diff --git a/README.md b/README.md index 288a87d..9af2449 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # 腾讯云 Flask Serverless Component -简体中文 | [English](./README.en.md) +简体中文 | [English](https://github.com/serverless-components/tencent-flask/tree/master/README.en.md) ## 简介 @@ -51,7 +51,7 @@ def user(id): 通过 npm 全局安装 [serverless cli](https://github.com/serverless/serverless) -```shell +```bash $ npm install -g serverless ``` @@ -59,7 +59,7 @@ $ npm install -g serverless 本地创建 `serverless.yml` 文件,在其中进行如下配置 -```shell +```bash $ touch serverless.yml ``` @@ -87,7 +87,7 @@ inputs: environment: release ``` -- [更多配置](./docs/configure.md) +- [更多配置](https://github.com/serverless-components/tencent-flask/tree/master/docs/configure.md) ### 3. 部署 @@ -95,7 +95,7 @@ inputs: 通过 `sls` 命令进行部署,并可以添加 `--debug` 参数查看部署过程中的信息 -```shell +```bash $ sls --debug ``` @@ -103,7 +103,7 @@ $ sls --debug 通过以下命令移除部署的 API 网关 -```shell +```bash $ sls remove --debug ``` @@ -111,7 +111,7 @@ $ sls remove --debug 当前默认支持 CLI 扫描二维码登录,如您希望配置持久的环境变量/秘钥信息,也可以本地创建 `.env` 文件 -```shell +```bash $ touch .env # 腾讯云的配置信息 ``` @@ -130,3 +130,9 @@ TENCENT_SECRET_KEY=123 ### 更多组件 可以在 [Serverless Components](https://github.com/serverless/components/blob/master/README.cn.md) repo 中查询更多组件的信息。 + +## License + +MIT License + +Copyright (c) 2020 Tencent Cloud, Inc. diff --git a/serverless.component.yml b/serverless.component.yml index 64f32f8..708d905 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: flask -version: 0.0.6 +version: 0.0.7 author: Tencent Cloud, Inc org: Tencent Cloud, Inc description: Deploy a serverless Flask application onto Tencent SCF and API Gateway. diff --git a/src/package.json b/src/package.json index a53fc4f..9931624 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "dependencies": { "download": "^8.0.0", - "tencent-component-toolkit": "^1.15.7", + "tencent-component-toolkit": "^1.16.3", "type": "^2.0.0" } } From e0de41d5960a32b234750c3b4e3500bc14adbbaa Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 2 Sep 2020 08:34:01 +0000 Subject: [PATCH 25/36] chore(release): version 0.0.7 ## [0.0.7](https://github.com/serverless-components/tencent-flask/compare/v0.0.6...v0.0.7) (2020-09-02) ### Bug Fixes * update tencnet-component-toolkit for api mark ([86e5a49](https://github.com/serverless-components/tencent-flask/commit/86e5a498820c8f0312405593033fa9b0590f1478)) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6353356..5555623 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.0.7](https://github.com/serverless-components/tencent-flask/compare/v0.0.6...v0.0.7) (2020-09-02) + + +### Bug Fixes + +* update tencnet-component-toolkit for api mark ([86e5a49](https://github.com/serverless-components/tencent-flask/commit/86e5a498820c8f0312405593033fa9b0590f1478)) + ## [0.0.6](https://github.com/serverless-components/tencent-flask/compare/v0.0.5...v0.0.6) (2020-09-02) From 65746555eed0c09ff48eb584afdc3e37e2c31872 Mon Sep 17 00:00:00 2001 From: yugasun Date: Wed, 2 Sep 2020 16:58:11 +0800 Subject: [PATCH 26/36] docs: update readme --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 9af2449..a582e9f 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ # 腾讯云 Flask Serverless Component -简体中文 | [English](https://github.com/serverless-components/tencent-flask/tree/master/README.en.md) - ## 简介 腾讯云 [Flask](https://github.com/pallets/flask) Serverless Component, 支持 Restful API 服务的部署,不支持 Flask Command. From fe034d170e434f9b3ac31c1b495957e6f5bf3f3e Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 7 Sep 2020 15:17:13 +0800 Subject: [PATCH 27/36] fix: update deploy flow for multi region --- README.md | 2 +- src/serverless.js | 241 ++++++++++++++++++++++-------------------- src/utils.js | 263 ++++++++++++++++------------------------------ 3 files changed, 220 insertions(+), 286 deletions(-) diff --git a/README.md b/README.md index a582e9f..17faaa5 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ inputs: 通过 `sls` 命令进行部署,并可以添加 `--debug` 参数查看部署过程中的信息 ```bash -$ sls --debug +$ sls deploy --debug ``` ### 4. 移除 diff --git a/src/serverless.js b/src/serverless.js index e961608..946563a 100644 --- a/src/serverless.js +++ b/src/serverless.js @@ -1,7 +1,7 @@ const { Component } = require('@serverless/core') -const { MultiApigw, Scf, Apigw, Cns } = require('tencent-component-toolkit') +const { Scf, Apigw, Cns, Cam } = require('tencent-component-toolkit') const { TypeError } = require('tencent-component-toolkit/src/utils/error') -const { uploadCodeToCos, getDefaultProtocol, deleteRecord, prepareInputs } = require('./utils') +const { uploadCodeToCos, getDefaultProtocol, prepareInputs, deepClone } = require('./utils') const CONFIGS = require('./config') class ServerlessComponent extends Component { @@ -27,135 +27,153 @@ class ServerlessComponent extends Component { } async deployFunction(credentials, inputs, regionList) { - const uploadCodeHandler = [] + if (!inputs.role) { + try { + const camClient = new Cam(credentials) + const roleExist = await camClient.CheckSCFExcuteRole() + if (roleExist) { + inputs.role = 'QCS_SCFExcuteRole' + } + } catch (e) { + // no op + } + } + const outputs = {} const appId = this.getAppId() - for (let eveRegionIndex = 0; eveRegionIndex < regionList.length; eveRegionIndex++) { - const curRegion = regionList[eveRegionIndex] - const funcDeployer = async () => { - const code = await uploadCodeToCos(this, appId, credentials, inputs, curRegion) - const scf = new Scf(credentials, curRegion) - const tempInputs = { - ...inputs, - code - } - const scfOutput = await scf.deploy(tempInputs) - outputs[curRegion] = { - functionName: scfOutput.FunctionName, - runtime: scfOutput.Runtime, - namespace: scfOutput.Namespace - } - - this.state[curRegion] = { - ...(this.state[curRegion] ? this.state[curRegion] : {}), - ...outputs[curRegion] - } + const funcDeployer = async (curRegion) => { + const code = await uploadCodeToCos(this, appId, credentials, inputs, curRegion) + const scf = new Scf(credentials, curRegion) + const tempInputs = { + ...inputs, + code + } + const scfOutput = await scf.deploy(deepClone(tempInputs)) + outputs[curRegion] = { + functionName: scfOutput.FunctionName, + runtime: scfOutput.Runtime, + namespace: scfOutput.Namespace + } - // default version is $LATEST - outputs[curRegion].lastVersion = scfOutput.LastVersion - ? scfOutput.LastVersion - : this.state.lastVersion || '$LATEST' - - // default traffic is 1.0, it can also be 0, so we should compare to undefined - outputs[curRegion].traffic = - scfOutput.Traffic !== undefined - ? scfOutput.Traffic - : this.state.traffic !== undefined - ? this.state.traffic - : 1 - - if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) { - outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion - this.state.configTrafficVersion = scfOutput.ConfigTrafficVersion - } + this.state[curRegion] = { + ...(this.state[curRegion] ? this.state[curRegion] : {}), + ...outputs[curRegion] + } - this.state.lastVersion = outputs[curRegion].lastVersion - this.state.traffic = outputs[curRegion].traffic + // default version is $LATEST + outputs[curRegion].lastVersion = scfOutput.LastVersion + ? scfOutput.LastVersion + : this.state.lastVersion || '$LATEST' + + // default traffic is 1.0, it can also be 0, so we should compare to undefined + outputs[curRegion].traffic = + scfOutput.Traffic !== undefined + ? scfOutput.Traffic + : this.state.traffic !== undefined + ? this.state.traffic + : 1 + + if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) { + outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion + this.state.configTrafficVersion = scfOutput.ConfigTrafficVersion } - uploadCodeHandler.push(funcDeployer()) + + this.state.lastVersion = outputs[curRegion].lastVersion + this.state.traffic = outputs[curRegion].traffic + } + + for (let i = 0; i < regionList.length; i++) { + const curRegion = regionList[i] + await funcDeployer(curRegion) } - await Promise.all(uploadCodeHandler) this.save() return outputs } + // try to add dns record + async tryToAddDnsRecord(credentials, customDomains) { + try { + const cns = new Cns(credentials) + for (let i = 0; i < customDomains.length; i++) { + const item = customDomains[i] + if (item.domainPrefix) { + await cns.deploy({ + domain: item.subDomain.replace(`${item.domainPrefix}.`, ''), + records: [ + { + subDomain: item.domainPrefix, + recordType: 'CNAME', + recordLine: '默认', + value: item.cname, + ttl: 600, + mx: 10, + status: 'enable' + } + ] + }) + } + } + } catch (e) { + console.log('METHOD_tryToAddDnsRecord', e.message) + } + } + async deployApigateway(credentials, inputs, regionList) { if (inputs.isDisabled) { return {} } - const apigw = new MultiApigw(credentials, regionList) - const oldState = this.state[regionList[0]] || {} - inputs.oldState = { - apiList: oldState.apiList || [], - customDomains: oldState.customDomains || [] + + const getServiceId = (instance, region) => { + const regionState = instance.state[region] + return inputs.serviceId || (regionState && regionState.serviceId) } - const apigwOutputs = await apigw.deploy(inputs) - const outputs = {} - Object.keys(apigwOutputs).forEach((curRegion) => { - const curOutput = apigwOutputs[curRegion] - outputs[curRegion] = { - serviceId: curOutput.serviceId, - subDomain: curOutput.subDomain, - environment: curOutput.environment, - url: `${getDefaultProtocol(inputs.protocols)}://${curOutput.subDomain}/${ - curOutput.environment - }/` - } - if (curOutput.customDomains) { - outputs[curRegion].customDomains = curOutput.customDomains - } - this.state[curRegion] = { - created: curOutput.created, - ...(this.state[curRegion] ? this.state[curRegion] : {}), - ...outputs[curRegion], - apiList: curOutput.apiList - } - }) - this.save() - return outputs - } - async deployCns(credentials, inputs, regionList, apigwOutputs) { - const cns = new Cns(credentials) - const cnsRegion = {} + const deployTasks = [] + const outputs = {} regionList.forEach((curRegion) => { - const curApigwOutput = apigwOutputs[curRegion] - cnsRegion[curRegion] = curApigwOutput.subDomain - }) + const apigwDeployer = async () => { + const apigw = new Apigw(credentials, curRegion) - const state = [] - const outputs = {} - const tempJson = {} - for (let i = 0; i < inputs.length; i++) { - const curCns = inputs[i] - for (let j = 0; j < curCns.records.length; j++) { - curCns.records[j].value = - cnsRegion[curCns.records[j].value.replace('temp_value_about_', '')] - } - const tencentCnsOutputs = await cns.deploy(curCns) - outputs[curCns.domain] = tencentCnsOutputs.DNS - ? tencentCnsOutputs.DNS - : 'The domain name has already been added.' - tencentCnsOutputs.domain = curCns.domain - state.push(tencentCnsOutputs) - } + const oldState = this.state[curRegion] || {} + const apigwInputs = { + ...inputs, + oldState: { + apiList: oldState.apiList || [], + customDomains: oldState.customDomains || [] + } + } + // different region deployment has different service id + apigwInputs.serviceId = getServiceId(this, curRegion) + const apigwOutput = await apigw.deploy(deepClone(apigwInputs)) + outputs[curRegion] = { + serviceId: apigwOutput.serviceId, + subDomain: apigwOutput.subDomain, + environment: apigwOutput.environment, + url: `${getDefaultProtocol(inputs.protocols)}://${apigwOutput.subDomain}/${ + apigwOutput.environment + }/` + } - // 删除serverless创建的但是不在本次列表中 - try { - for (let i = 0; i < state.length; i++) { - tempJson[state[i].domain] = state[i].records - } - const recordHistory = this.state.cns || [] - for (let i = 0; i < recordHistory.length; i++) { - const delList = deleteRecord(tempJson[recordHistory[i].domain], recordHistory[i].records) - if (delList && delList.length > 0) { - await cns.remove({ deleteList: delList }) + if (apigwOutput.customDomains) { + // TODO: need confirm add cns authentication + if (inputs.autoAddDnsRecord === true) { + // await this.tryToAddDnsRecord(credentials, apigwOutput.customDomains) + } + outputs[curRegion].customDomains = apigwOutput.customDomains + } + this.state[curRegion] = { + created: true, + ...(this.state[curRegion] ? this.state[curRegion] : {}), + ...outputs[curRegion], + apiList: apigwOutput.apiList } } - } catch (e) {} + deployTasks.push(apigwDeployer()) + }) + + await Promise.all(deployTasks) - this.state['cns'] = state this.save() return outputs } @@ -166,7 +184,7 @@ class ServerlessComponent extends Component { const credentials = this.getCredentials() // 对Inputs内容进行标准化 - const { regionList, functionConf, apigatewayConf, cnsConf } = await prepareInputs( + const { regionList, functionConf, apigatewayConf } = await prepareInputs( this, credentials, inputs @@ -198,11 +216,6 @@ class ServerlessComponent extends Component { outputs['scf'] = functionOutputs } - // cns depends on apigw, so if disabled apigw, just ignore it. - if (cnsConf.length > 0 && apigatewayConf.isDisabled !== true) { - outputs['cns'] = await this.deployCns(credentials, cnsConf, regionList, apigwOutputs) - } - this.state.region = regionList[0] this.state.regionList = regionList this.state.lambdaArn = functionConf.name diff --git a/src/utils.js b/src/utils.js index c613450..ef8aa9f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,6 +1,6 @@ const path = require('path') const fs = require('fs') -const { Domain, Cos } = require('tencent-component-toolkit') +const { Cos } = require('tencent-component-toolkit') const ensureObject = require('type/object/ensure') const ensureIterable = require('type/iterable/ensure') const ensureString = require('type/string/ensure') @@ -16,10 +16,45 @@ const generateId = () => .toString(36) .substring(6) +const deepClone = (obj) => { + return JSON.parse(JSON.stringify(obj)) +} + const getType = (obj) => { return Object.prototype.toString.call(obj).slice(8, -1) } +const mergeJson = (sourceJson, targetJson) => { + Object.entries(sourceJson).forEach(([key, val]) => { + targetJson[key] = deepClone(val) + }) + return targetJson +} + +const capitalString = (str) => { + if (str.length < 2) { + return str.toUpperCase() + } + + return `${str[0].toUpperCase()}${str.slice(1)}` +} + +const getDefaultProtocol = (protocols) => { + return String(protocols).includes('https') ? 'https' : 'http' +} + +const getDefaultFunctionName = () => { + return `${CONFIGS.compName}_component_${generateId()}` +} + +const getDefaultServiceName = () => { + return 'serverless' +} + +const getDefaultServiceDescription = () => { + return 'Created by Serverless Component' +} + const validateTraffic = (num) => { if (getType(num) !== 'Number') { throw new TypeError( @@ -118,10 +153,16 @@ const uploadCodeToCos = async (instance, appId, credentials, inputs, region) => object: objectName, method: 'PUT' }) - const shimFiles = await getDirFiles(path.join(__dirname, '_shims')) + // if shims and sls sdk entries had been injected to zipPath, no need to injected again console.log(`Uploading code to bucket ${bucketName}`) - await instance.uploadSourceZipToCOS(zipPath, uploadUrl, shimFiles, {}) + if (instance.codeInjected === true) { + await instance.uploadSourceZipToCOS(zipPath, uploadUrl, {}, {}) + } else { + const shimFiles = await getDirFiles(path.join(__dirname, '_shims')) + await instance.uploadSourceZipToCOS(zipPath, uploadUrl, shimFiles, {}) + instance.codeInjected = true + } console.log(`Upload ${objectName} to bucket ${bucketName} success`) } } @@ -136,69 +177,6 @@ const uploadCodeToCos = async (instance, appId, credentials, inputs, region) => } } -const mergeJson = (sourceJson, targetJson) => { - for (const eveKey in sourceJson) { - if (targetJson.hasOwnProperty(eveKey)) { - if (['protocols', 'endpoints', 'customDomain'].indexOf(eveKey) != -1) { - for (let i = 0; i < sourceJson[eveKey].length; i++) { - const sourceEvents = JSON.stringify(sourceJson[eveKey][i]) - const targetEvents = JSON.stringify(targetJson[eveKey]) - if (targetEvents.indexOf(sourceEvents) == -1) { - targetJson[eveKey].push(sourceJson[eveKey][i]) - } - } - } else { - if (typeof sourceJson[eveKey] != 'string') { - mergeJson(sourceJson[eveKey], targetJson[eveKey]) - } else { - targetJson[eveKey] = sourceJson[eveKey] - } - } - } else { - targetJson[eveKey] = sourceJson[eveKey] - } - } - return targetJson -} - -const capitalString = (str) => { - if (str.length < 2) { - return str.toUpperCase() - } - - return `${str[0].toUpperCase()}${str.slice(1)}` -} - -const getDefaultProtocol = (protocols) => { - if (protocols.map((i) => i.toLowerCase()).includes('https')) { - return 'https' - } - return 'http' -} - -const deleteRecord = (newRecords, historyRcords) => { - const deleteList = [] - for (let i = 0; i < historyRcords.length; i++) { - let temp = false - for (let j = 0; j < newRecords.length; j++) { - if ( - newRecords[j].domain == historyRcords[i].domain && - newRecords[j].subDomain == historyRcords[i].subDomain && - newRecords[j].recordType == historyRcords[i].recordType && - newRecords[j].value == historyRcords[i].value && - newRecords[j].recordLine == historyRcords[i].recordLine - ) { - temp = true - break - } - } - if (!temp) { - deleteList.push(historyRcords[i]) - } - } - return deleteList -} - const prepareInputs = async (instance, credentials, inputs = {}) => { // 对function inputs进行标准化 const tempFunctionConf = inputs.functionConf ? inputs.functionConf : {} @@ -212,9 +190,6 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { // chenck state function name const stateFunctionName = instance.state[regionList[0]] && instance.state[regionList[0]].functionName - // check state service id - const stateServiceId = instance.state[regionList[0]] && instance.state[regionList[0]].serviceId - const functionConf = { code: { src: inputs.src, @@ -224,7 +199,7 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { name: ensureString(inputs.functionName, { isOptional: true }) || stateFunctionName || - `${CONFIGS.compName}_component_${generateId()}`, + getDefaultFunctionName(), region: regionList, role: ensureString(tempFunctionConf.role ? tempFunctionConf.role : inputs.role, { default: '' @@ -256,7 +231,12 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { traffic: inputs.traffic, lastVersion: instance.state.lastVersion, eip: tempFunctionConf.eip === true, - l5Enable: tempFunctionConf.l5Enable === true + l5Enable: tempFunctionConf.l5Enable === true, + timeout: tempFunctionConf.timeout ? tempFunctionConf.timeout : CONFIGS.timeout, + memorySize: tempFunctionConf.memorySize ? tempFunctionConf.memorySize : CONFIGS.memorySize, + tags: ensureObject(tempFunctionConf.tags ? tempFunctionConf.tags : inputs.tag, { + default: null + }) } // validate traffic @@ -265,73 +245,55 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { } functionConf.needSetTraffic = inputs.traffic !== undefined && functionConf.lastVersion - functionConf.tags = ensureObject(tempFunctionConf.tags ? tempFunctionConf.tags : inputs.tag, { - default: null - }) - - if (inputs.functionConf) { - functionConf.timeout = inputs.functionConf.timeout - ? inputs.functionConf.timeout - : CONFIGS.timeout - functionConf.memorySize = inputs.functionConf.memorySize - ? inputs.functionConf.memorySize - : CONFIGS.memorySize - if (inputs.functionConf.environment) { - functionConf.environment = inputs.functionConf.environment - } - if (inputs.functionConf.vpcConfig) { - functionConf.vpcConfig = inputs.functionConf.vpcConfig - } + if (tempFunctionConf.environment) { + functionConf.environment = inputs.functionConf.environment + } + if (tempFunctionConf.vpcConfig) { + functionConf.vpcConfig = inputs.functionConf.vpcConfig } // 对apigw inputs进行标准化 - const apigatewayConf = inputs.apigatewayConf ? inputs.apigatewayConf : {} - apigatewayConf.isDisabled = inputs.apigatewayConf === true - apigatewayConf.fromClientRemark = fromClientRemark - apigatewayConf.serviceName = inputs.serviceName - apigatewayConf.description = `Serverless Framework Tencent-${capitalString( - CONFIGS.compName - )} Component` - apigatewayConf.serviceId = inputs.serviceId || stateServiceId - apigatewayConf.region = functionConf.region - apigatewayConf.protocols = apigatewayConf.protocols || ['http'] - apigatewayConf.environment = apigatewayConf.environment ? apigatewayConf.environment : 'release' - apigatewayConf.endpoints = [ - { - path: '/', - enableCORS: apigatewayConf.enableCORS, - serviceTimeout: apigatewayConf.serviceTimeout, - method: 'ANY', - function: { - isIntegratedResponse: apigatewayConf.isIntegratedResponse === false ? false : true, - functionName: functionConf.name, - functionNamespace: functionConf.namespace, - functionQualifier: functionConf.needSetTraffic ? '$DEFAULT' : '$LATEST' + const tempApigwConf = inputs.apigatewayConf ? inputs.apigatewayConf : {} + const apigatewayConf = { + serviceId: inputs.serviceId, + region: regionList, + isDisabled: tempApigwConf.isDisabled === true, + fromClientRemark: fromClientRemark, + serviceName: inputs.serviceName || getDefaultServiceName(instance), + description: getDefaultServiceDescription(instance), + protocols: tempApigwConf.protocols || ['http'], + environment: tempApigwConf.environment ? tempApigwConf.environment : 'release', + endpoints: [ + { + path: '/', + enableCORS: tempApigwConf.enableCORS, + serviceTimeout: tempApigwConf.serviceTimeout, + method: 'ANY', + function: { + isIntegratedResponse: true, + functionName: functionConf.name, + functionNamespace: functionConf.namespace + } } - } - ] - if (apigatewayConf.usagePlan) { + ], + customDomains: tempApigwConf.customDomains || [] + } + if (tempApigwConf.usagePlan) { apigatewayConf.endpoints[0].usagePlan = { - usagePlanId: apigatewayConf.usagePlan.usagePlanId, - usagePlanName: apigatewayConf.usagePlan.usagePlanName, - usagePlanDesc: apigatewayConf.usagePlan.usagePlanDesc, - maxRequestNum: apigatewayConf.usagePlan.maxRequestNum + usagePlanId: tempApigwConf.usagePlan.usagePlanId, + usagePlanName: tempApigwConf.usagePlan.usagePlanName, + usagePlanDesc: tempApigwConf.usagePlan.usagePlanDesc, + maxRequestNum: tempApigwConf.usagePlan.maxRequestNum } } - if (apigatewayConf.auth) { + if (tempApigwConf.auth) { apigatewayConf.endpoints[0].auth = { - secretName: apigatewayConf.auth.secretName, - secretIds: apigatewayConf.auth.secretIds + secretName: tempApigwConf.auth.secretName, + secretIds: tempApigwConf.auth.secretIds } } - // 对cns inputs进行标准化 - const tempCnsConf = {} - const tempCnsBaseConf = inputs.cloudDNSConf ? inputs.cloudDNSConf : {} - - // 分地域处理functionConf/apigatewayConf/cnsConf - for (let i = 0; i < functionConf.region.length; i++) { - const curRegion = functionConf.region[i] + regionList.forEach((curRegion) => { const curRegionConf = inputs[curRegion] if (curRegionConf && curRegionConf.functionConf) { functionConf[curRegion] = curRegionConf.functionConf @@ -339,62 +301,21 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { if (curRegionConf && curRegionConf.apigatewayConf) { apigatewayConf[curRegion] = curRegionConf.apigatewayConf } - - const tempRegionCnsConf = mergeJson( - tempCnsBaseConf, - curRegionConf && curRegionConf.cloudDNSConf ? curRegionConf.cloudDNSConf : {} - ) - - tempCnsConf[functionConf.region[i]] = { - recordType: 'CNAME', - recordLine: tempRegionCnsConf.recordLine ? tempRegionCnsConf.recordLine : undefined, - ttl: tempRegionCnsConf.ttl, - mx: tempRegionCnsConf.mx, - status: tempRegionCnsConf.status ? tempRegionCnsConf.status : 'enable' - } - } - - const cnsConf = [] - // 对cns inputs进行检查和赋值 - if (apigatewayConf.customDomain && apigatewayConf.customDomain.length > 0) { - const domain = new Domain(credentials) - for (let domianNum = 0; domianNum < apigatewayConf.customDomain.length; domianNum++) { - const domainData = await domain.check(apigatewayConf.customDomain[domianNum].domain) - const tempInputs = { - domain: domainData.domain, - records: [] - } - for (let eveRecordNum = 0; eveRecordNum < functionConf.region.length; eveRecordNum++) { - if (tempCnsConf[functionConf.region[eveRecordNum]].recordLine) { - tempInputs.records.push({ - subDomain: domainData.subDomain || '@', - recordType: 'CNAME', - recordLine: tempCnsConf[functionConf.region[eveRecordNum]].recordLine, - value: `temp_value_about_${functionConf.region[eveRecordNum]}`, - ttl: tempCnsConf[functionConf.region[eveRecordNum]].ttl, - mx: tempCnsConf[functionConf.region[eveRecordNum]].mx, - status: tempCnsConf[functionConf.region[eveRecordNum]].status || 'enable' - }) - } - } - cnsConf.push(tempInputs) - } - } + }) return { regionList, functionConf, - apigatewayConf, - cnsConf + apigatewayConf } } module.exports = { + deepClone, generateId, uploadCodeToCos, mergeJson, capitalString, getDefaultProtocol, - deleteRecord, prepareInputs } From 821f3e65312332335eb804caefdc8fd928b618aa Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 7 Sep 2020 15:18:10 +0800 Subject: [PATCH 28/36] fix: update deps --- serverless.component.yml | 12 ++++++------ src/package.json | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/serverless.component.yml b/serverless.component.yml index 708d905..aed1ef3 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,11 +1,11 @@ name: flask -version: 0.0.7 -author: Tencent Cloud, Inc -org: Tencent Cloud, Inc +version: 0.0.8 +author: 'Tencent Cloud, Inc' +org: 'Tencent Cloud, Inc' description: Deploy a serverless Flask application onto Tencent SCF and API Gateway. -keywords: tencent, serverless, flask -repo: https://github.com/serverless-components/tencent-flask -readme: https://github.com/serverless-components/tencent-flask/tree/master/README.md +keywords: 'tencent, serverless, flask' +repo: 'https://github.com/serverless-components/tencent-flask' +readme: 'https://github.com/serverless-components/tencent-flask/tree/master/README.md' license: MIT main: ./src webDeployable: true diff --git a/src/package.json b/src/package.json index 9931624..4e09522 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "dependencies": { "download": "^8.0.0", - "tencent-component-toolkit": "^1.16.3", - "type": "^2.0.0" + "tencent-component-toolkit": "^1.16.4", + "type": "^2.1.0" } } From 49bc914fad091bad9202ac481042760509342b3d Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 7 Sep 2020 07:19:58 +0000 Subject: [PATCH 29/36] chore(release): version 0.0.8 ## [0.0.8](https://github.com/serverless-components/tencent-flask/compare/v0.0.7...v0.0.8) (2020-09-07) ### Bug Fixes * update deploy flow for multi region ([fe034d1](https://github.com/serverless-components/tencent-flask/commit/fe034d170e434f9b3ac31c1b495957e6f5bf3f3e)) * update deps ([821f3e6](https://github.com/serverless-components/tencent-flask/commit/821f3e65312332335eb804caefdc8fd928b618aa)) --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5555623..4dad61f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [0.0.8](https://github.com/serverless-components/tencent-flask/compare/v0.0.7...v0.0.8) (2020-09-07) + + +### Bug Fixes + +* update deploy flow for multi region ([fe034d1](https://github.com/serverless-components/tencent-flask/commit/fe034d170e434f9b3ac31c1b495957e6f5bf3f3e)) +* update deps ([821f3e6](https://github.com/serverless-components/tencent-flask/commit/821f3e65312332335eb804caefdc8fd928b618aa)) + ## [0.0.7](https://github.com/serverless-components/tencent-flask/compare/v0.0.6...v0.0.7) (2020-09-02) From 4b3c40cb9e9f5f586a9d781cbae523112dfdebc0 Mon Sep 17 00:00:00 2001 From: Yuga Sun Date: Tue, 15 Dec 2020 15:06:49 +0800 Subject: [PATCH 30/36] fix: update deploy and remove flow (#14) * fix: update to deployment to serial flow * fix: update remove flow --- .github/workflows/release.yml | 48 +++++++++++++++++++ .github/workflows/test.yml | 47 ++++++++++++++++++ .github/workflows/validate.yml | 45 +++++++++++++++++ .travis.yml | 23 --------- .../index.test.js | 17 +++---- {tests => __tests__/lib}/utils.js | 0 jest.config.js | 14 ++++++ package.json | 3 +- serverless.component.yml | 2 +- src/package.json | 2 +- src/serverless.js | 27 +++++++---- 11 files changed, 182 insertions(+), 46 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml create mode 100644 .github/workflows/validate.yml delete mode 100644 .travis.yml rename tests/integration.test.js => __tests__/index.test.js (77%) rename {tests => __tests__/lib}/utils.js (100%) create mode 100644 jest.config.js diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..11c8ac8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,48 @@ +name: Release + +on: + push: + branches: [master] + +jobs: + release: + name: Release + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + persist-credentials: false + + - name: Install Node.js and npm + uses: actions/setup-node@v1 + with: + node-version: 14.x + registry-url: https://registry.npmjs.org + + - name: Retrieve dependencies from cache + id: cacheNpm + uses: actions/cache@v2 + with: + path: | + ~/.npm + node_modules + key: npm-v14-${{ runner.os }}-refs/heads/master-${{ hashFiles('package.json') }} + restore-keys: npm-v14-${{ runner.os }}-refs/heads/master- + + - name: Install dependencies + if: steps.cacheNpm.outputs.cache-hit != 'true' + run: | + npm update --no-save + npm update --save-dev --no-save + - name: Releasing + run: | + npm run release + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + GIT_AUTHOR_NAME: slsplus + GIT_AUTHOR_EMAIL: slsplus.sz@gmail.com + GIT_COMMITTER_NAME: slsplus + GIT_COMMITTER_EMAIL: slsplus.sz@gmail.com diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..c966e94 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,47 @@ +name: Test + +on: + pull_request: + branches: [master] + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # Ensure connection with 'master' branch + fetch-depth: 2 + + - name: Install Node.js and npm + uses: actions/setup-node@v1 + with: + node-version: 14.x + registry-url: https://registry.npmjs.org + + - name: Retrieve dependencies from cache + id: cacheNpm + uses: actions/cache@v2 + with: + path: | + ~/.npm + node_modules + key: npm-v14-${{ runner.os }}-${{ github.ref }}-${{ hashFiles('package.json') }} + restore-keys: | + npm-v14-${{ runner.os }}-${{ github.ref }}- + npm-v14-${{ runner.os }}-refs/heads/master- + + - name: Install dependencies + if: steps.cacheNpm.outputs.cache-hit != 'true' + run: | + npm update --no-save + npm update --save-dev --no-save + - name: Running tests + run: npm run test + env: + SERVERLESS_PLATFORM_VENDOR: tencent + GLOBAL_ACCELERATOR_NA: true + TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }} + TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }} diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..3840792 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,45 @@ +name: Validate + +on: + pull_request: + branches: [master] + +jobs: + lintAndFormatting: + name: Lint & Formatting + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # Ensure connection with 'master' branch + fetch-depth: 2 + + - name: Install Node.js and npm + uses: actions/setup-node@v1 + with: + node-version: 14.x + registry-url: https://registry.npmjs.org + + - name: Retrieve dependencies from cache + id: cacheNpm + uses: actions/cache@v2 + with: + path: | + ~/.npm + node_modules + key: npm-v14-${{ runner.os }}-${{ github.ref }}-${{ hashFiles('package.json') }} + restore-keys: | + npm-v14-${{ runner.os }}-${{ github.ref }}- + npm-v14-${{ runner.os }}-refs/heads/master- + + - name: Install dependencies + if: steps.cacheNpm.outputs.cache-hit != 'true' + run: | + npm update --no-save + npm update --save-dev --no-save + + - name: Validate Formatting + run: npm run prettier:fix + - name: Validate Lint rules + run: npm run lint:fix diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d78fad9..0000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: node_js - -node_js: - - 10 - -install: - - npm install - -# should change to serverless registry publish -jobs: - include: - # Define the release stage that runs semantic-release - - stage: release - node_js: 10.18 - # Advanced: optionally overwrite your default `script` step to skip the tests - # script: skip - deploy: - provider: script - skip_cleanup: true - on: - branch: master - script: - - npm run release diff --git a/tests/integration.test.js b/__tests__/index.test.js similarity index 77% rename from tests/integration.test.js rename to __tests__/index.test.js index 5c6215a..3c172e5 100644 --- a/tests/integration.test.js +++ b/__tests__/index.test.js @@ -1,13 +1,9 @@ -const { generateId, getServerlessSdk } = require('./utils') +const { generateId, getServerlessSdk } = require('./lib/utils') -// set enough timeout for deployment to finish -jest.setTimeout(300000) - -// the yaml file we're testing against const instanceYaml = { org: 'orgDemo', app: 'appDemo', - component: 'flask', + component: 'flask@dev', name: `flask-integration-tests-${generateId()}`, stage: 'dev', inputs: { @@ -17,16 +13,17 @@ const instanceYaml = { } } -// get credentials from process.env but need to init empty credentials object const credentials = { - tencent: {} + tencent: { + SecretId: process.env.TENCENT_SECRET_ID, + SecretKey: process.env.TENCENT_SECRET_KEY, + } } -// get serverless construct sdk const sdk = getServerlessSdk(instanceYaml.org) it('should successfully deploy flask app', async () => { - const instance = await sdk.deploy(instanceYaml, { tencent: {} }) + const instance = await sdk.deploy(instanceYaml, credentials) expect(instance).toBeDefined() expect(instance.instanceName).toEqual(instanceYaml.name) // get src from template by default diff --git a/tests/utils.js b/__tests__/lib/utils.js similarity index 100% rename from tests/utils.js rename to __tests__/lib/utils.js diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..a70dd57 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,14 @@ +const { join } = require('path') +require('dotenv').config({ path: join(__dirname, '.env.test') }) + +const config = { + verbose: true, + silent: false, + testTimeout: 600000, + testEnvironment: 'node', + testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(js|ts)$', + testPathIgnorePatterns: ['/node_modules/', '/__tests__/lib/'], + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'] +} + +module.exports = config diff --git a/package.json b/package.json index 6369aff..6ad2369 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,7 @@ "access": "public" }, "scripts": { - "int-test": "jest ./tests/integration.test.js --testEnvironment node", - "test": "npm run lint && npm run prettier && npm run int-test", + "test": "jest", "commitlint": "commitlint -f HEAD@{15}", "lint": "eslint --ext .js,.ts,.tsx .", "lint:fix": "eslint --fix --ext .js,.ts,.tsx .", diff --git a/serverless.component.yml b/serverless.component.yml index aed1ef3..c2b404f 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: flask -version: 0.0.8 +version: 0.0.9 author: 'Tencent Cloud, Inc' org: 'Tencent Cloud, Inc' description: Deploy a serverless Flask application onto Tencent SCF and API Gateway. diff --git a/src/package.json b/src/package.json index 4e09522..850fa0c 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "dependencies": { "download": "^8.0.0", - "tencent-component-toolkit": "^1.16.4", + "tencent-component-toolkit": "^1.19.8", "type": "^2.1.0" } } diff --git a/src/serverless.js b/src/serverless.js index 946563a..bdd8375 100644 --- a/src/serverless.js +++ b/src/serverless.js @@ -196,24 +196,33 @@ class ServerlessComponent extends Component { outputs.templateUrl = CONFIGS.templateUrl } - const deployTasks = [this.deployFunction(credentials, functionConf, regionList, outputs)] + let apigwOutputs + const functionOutputs = await this.deployFunction( + credentials, + functionConf, + regionList, + outputs + ) // support apigatewayConf.isDisabled if (apigatewayConf.isDisabled !== true) { - deployTasks.push(this.deployApigateway(credentials, apigatewayConf, regionList, outputs)) + apigwOutputs = await this.deployApigateway(credentials, apigatewayConf, regionList, outputs) } else { this.state.apigwDisabled = true } - const [functionOutputs, apigwOutputs = {}] = await Promise.all(deployTasks) // optimize outputs for one region if (regionList.length === 1) { const [oneRegion] = regionList outputs.region = oneRegion - outputs['apigw'] = apigwOutputs[oneRegion] outputs['scf'] = functionOutputs[oneRegion] + if (apigwOutputs) { + outputs['apigw'] = apigwOutputs[oneRegion] + } } else { - outputs['apigw'] = apigwOutputs outputs['scf'] = functionOutputs + if (apigwOutputs) { + outputs['apigw'] = apigwOutputs + } } this.state.region = regionList[0] @@ -238,10 +247,6 @@ class ServerlessComponent extends Component { const scf = new Scf(credentials, curRegion) const apigw = new Apigw(credentials, curRegion) const handler = async () => { - await scf.remove({ - functionName: curState.functionName, - namespace: curState.namespace - }) // if disable apigw, no need to remove if (state.apigwDisabled !== true) { await apigw.remove({ @@ -252,6 +257,10 @@ class ServerlessComponent extends Component { customDomains: curState.customDomains }) } + await scf.remove({ + functionName: curState.functionName, + namespace: curState.namespace + }) } removeHandlers.push(handler()) } From 61eff4fd5e52a203886848067480bf2bcea9e0d9 Mon Sep 17 00:00:00 2001 From: slsplus Date: Tue, 15 Dec 2020 07:07:53 +0000 Subject: [PATCH 31/36] chore(release): version 0.0.9 ## [0.0.9](https://github.com/serverless-components/tencent-flask/compare/v0.0.8...v0.0.9) (2020-12-15) ### Bug Fixes * update deploy and remove flow ([#14](https://github.com/serverless-components/tencent-flask/issues/14)) ([4b3c40c](https://github.com/serverless-components/tencent-flask/commit/4b3c40cb9e9f5f586a9d781cbae523112dfdebc0)) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dad61f..0a8e888 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.0.9](https://github.com/serverless-components/tencent-flask/compare/v0.0.8...v0.0.9) (2020-12-15) + + +### Bug Fixes + +* update deploy and remove flow ([#14](https://github.com/serverless-components/tencent-flask/issues/14)) ([4b3c40c](https://github.com/serverless-components/tencent-flask/commit/4b3c40cb9e9f5f586a9d781cbae523112dfdebc0)) + ## [0.0.8](https://github.com/serverless-components/tencent-flask/compare/v0.0.7...v0.0.8) (2020-09-07) From 744ccd08f2f13c06bdbf25539ec557573f69c1b4 Mon Sep 17 00:00:00 2001 From: Donghui <977675308@qq.com> Date: Mon, 21 Dec 2020 19:08:52 +0800 Subject: [PATCH 32/36] docs: typo Flash --> Flask (#15) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 17faaa5..463bef4 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ app = Flask(__name__) @app.route("/") def index(): - return "Hello Flash" + return "Hello Flask" @app.route("/users") def users(): From ed4dcfc25c5542d54c9210f2982ad6ee1a8dcd9a Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 25 Jan 2021 10:40:17 +0800 Subject: [PATCH 33/36] chore: add local start for example --- example/README.md | 9 +++++++++ example/app.py | 5 +++++ 2 files changed, 14 insertions(+) create mode 100644 example/README.md diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..b78aea5 --- /dev/null +++ b/example/README.md @@ -0,0 +1,9 @@ +# Flask example + +本地启动服务: + +```bash +$ ENV=local python app.py +``` + +可以发现 `app.py` 中通过判断环境变量 `ENV` 为 `local` 才启动服务,云函数运行时就不会启动服务。 diff --git a/example/app.py b/example/app.py index cd62520..b932647 100644 --- a/example/app.py +++ b/example/app.py @@ -1,3 +1,4 @@ +import os from flask import Flask, jsonify app = Flask(__name__) @@ -16,3 +17,7 @@ def users(): @app.route("/users/") def user(id): return jsonify(data={'name': 'test1'}) + +isLocal = os.getenv('ENV') == 'local' +if isLocal: + app.run(host='0.0.0.0',port=3000,debug=True) From 97043cb2d0a1d66d448f216309aac92289cc2e7d Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 2 Feb 2021 17:25:52 +0800 Subject: [PATCH 34/36] fix: multi cookie bug --- serverless.component.yml | 2 +- src/_shims/severless_wsgi.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/serverless.component.yml b/serverless.component.yml index c2b404f..8a37f6c 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: flask -version: 0.0.9 +version: 0.0.10 author: 'Tencent Cloud, Inc' org: 'Tencent Cloud, Inc' description: Deploy a serverless Flask application onto Tencent SCF and API Gateway. diff --git a/src/_shims/severless_wsgi.py b/src/_shims/severless_wsgi.py index 2de91a3..71e0a94 100644 --- a/src/_shims/severless_wsgi.py +++ b/src/_shims/severless_wsgi.py @@ -55,11 +55,13 @@ def split_headers(headers): """ new_headers = {} - for key in headers.keys(): + for key in set(headers.keys()): values = headers.get_all(key) - if len(values) > 1: + if len(values) > 1 and key.lower() != 'set-cookie': for value, casing in zip(values, all_casings(key)): new_headers[casing] = value + elif key.lower() == 'set-cookie': + new_headers[key] = values elif len(values) == 1: new_headers[key] = values[0] From e3a0f6176e89be2904d6c3aa4bf2901307b3a050 Mon Sep 17 00:00:00 2001 From: slsplus Date: Tue, 2 Feb 2021 09:30:21 +0000 Subject: [PATCH 35/36] chore(release): version 0.0.10 ## [0.0.10](https://github.com/serverless-components/tencent-flask/compare/v0.0.9...v0.0.10) (2021-02-02) ### Bug Fixes * multi cookie bug ([97043cb](https://github.com/serverless-components/tencent-flask/commit/97043cb2d0a1d66d448f216309aac92289cc2e7d)) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a8e888..677f120 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.0.10](https://github.com/serverless-components/tencent-flask/compare/v0.0.9...v0.0.10) (2021-02-02) + + +### Bug Fixes + +* multi cookie bug ([97043cb](https://github.com/serverless-components/tencent-flask/commit/97043cb2d0a1d66d448f216309aac92289cc2e7d)) + ## [0.0.9](https://github.com/serverless-components/tencent-flask/compare/v0.0.8...v0.0.9) (2020-12-15) From 75d5f466c293c315837d49523ed1854f661ac0e0 Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 22 Mar 2021 16:10:07 +0800 Subject: [PATCH 36/36] docs: update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 463bef4..6fc4cf8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +⚠️⚠️⚠️ 所有框架组件项目迁移到 [tencent-framework-components](https://github.com/serverless-components/tencent-framework-components). + [![Serverless Python Flask Tencent Cloud](https://img.serverlesscloud.cn/20191226/1577347052683-flask_%E9%95%BF.png)](http://serverless.com) # 腾讯云 Flask Serverless Component