diff --git a/package.json b/package.json index c06135d..0bd16eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tencent-component-toolkit", - "version": "2.27.0", + "version": "2.27.1", "description": "Tencent component toolkit", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/modules/scf/config.ts b/src/modules/scf/config.ts index 4f72d16..13d29bf 100644 --- a/src/modules/scf/config.ts +++ b/src/modules/scf/config.ts @@ -6,6 +6,7 @@ const CONFIGS = { defaultInitTimeout: 3, waitStatus: ['Creating', 'Updating', 'Publishing', 'Deleting'], failStatus: ['CreateFailed ', 'UpdateFailed', 'PublishFailed', 'DeleteFailed'], + defaultDiskSize: 512, }; export default CONFIGS; diff --git a/src/modules/scf/entities/alias.ts b/src/modules/scf/entities/alias.ts index c1f09ea..117718a 100644 --- a/src/modules/scf/entities/alias.ts +++ b/src/modules/scf/entities/alias.ts @@ -20,14 +20,17 @@ export default class AliasEntity extends BaseEntity { Name: inputs.aliasName, Namespace: inputs.namespace || 'default', Description: inputs.description || 'Published by Serverless Component', + RoutingConfig: { + AdditionalVersionWeights: inputs.additionalVersions + ? inputs.additionalVersions?.map((v) => { + return { + Version: v.version, + Weight: v.weight, + }; + }) + : [], + }, }; - if (inputs.lastVersion && inputs.traffic) { - publishInputs.RoutingConfig = { - AdditionalVersionWeights: [ - { Version: inputs.lastVersion, Weight: strip(1 - inputs.traffic) }, - ], - }; - } const Response = await this.request(publishInputs); return Response; } @@ -43,12 +46,14 @@ export default class AliasEntity extends BaseEntity { Name: inputs.aliasName || '$DEFAULT', Namespace: inputs.namespace || 'default', RoutingConfig: { - AdditionalVersionWeights: inputs.additionalVersions?.map((v) => { - return { - Version: v.version, - Weight: v.weight, - }; - }), + AdditionalVersionWeights: inputs.additionalVersions + ? inputs.additionalVersions?.map((v) => { + return { + Version: v.version, + Weight: v.weight, + }; + }) + : [], }, Description: inputs.description || 'Configured by Serverless Component', }; diff --git a/src/modules/scf/index.ts b/src/modules/scf/index.ts index 49cfc99..96fe77c 100644 --- a/src/modules/scf/index.ts +++ b/src/modules/scf/index.ts @@ -280,6 +280,21 @@ export default class Scf { const functionName = inputs.name; const { ignoreTriggers = false } = inputs; + if (inputs?.aliasName) { + if (!inputs?.additionalVersionWeights) { + throw new ApiTypeError( + 'PARAMETER_SCF', + 'additionalVersionWeights is required when aliasName is setted', + ); + } + if (!inputs.publish && !inputs?.aliasFunctionVersion) { + throw new ApiTypeError( + 'PARAMETER_SCF', + 'aliasFunctionVersion is required when aliasName is setted', + ); + } + } + // 在部署前,检查函数初始状态,如果初始为 CreateFailed,尝试先删除,再重新创建 let funcInfo = await this.scf.getInitialStatus({ namespace, functionName }); @@ -311,6 +326,11 @@ export default class Scf { namespace, description: inputs.publishDescription, }); + + if (inputs.aliasName) { + inputs.aliasFunctionVersion = FunctionVersion; + } + inputs.lastVersion = FunctionVersion; outputs.LastVersion = FunctionVersion; @@ -321,13 +341,10 @@ export default class Scf { }); } - const aliasAddionalVersion = inputs.aliasAddionalVersion || inputs.lastVersion; - const needSetTraffic = - inputs.traffic != null && aliasAddionalVersion && aliasAddionalVersion !== '$LATEST'; - const needSetAlias = (inputs.aliasName && inputs.aliasName !== '$DEFAULT') || needSetTraffic; - if (needSetAlias) { + // 检测配置的别名是否存在,不存在就创建,存在的话就设置流量 + if (inputs.aliasName) { let needCreateAlias = false; - if (inputs.aliasName && inputs.aliasName !== '$DEFAULT') { + if (inputs.aliasName !== '$DEFAULT') { try { const aliasInfo = await this.alias.get({ namespace, @@ -347,32 +364,51 @@ export default class Scf { } } } - if (needCreateAlias) { - await this.alias.create({ - namespace, - functionName, - functionVersion: inputs.aliasFunctionVersion || funcInfo?.Qualifier, - aliasName: inputs.aliasName!, - lastVersion: aliasAddionalVersion!, - traffic: inputs.traffic!, - description: inputs.aliasDescription, - }); - } else { + try { + // 创建别名 + if (needCreateAlias) { + await this.alias.create({ + namespace, + functionName, + functionVersion: inputs.aliasFunctionVersion || funcInfo?.Qualifier, + aliasName: inputs.aliasName!, + description: inputs.aliasDescription, + additionalVersions: inputs.additionalVersionWeights, + }); + } else { + // 更新别名 + await this.alias.update({ + namespace, + functionName, + functionVersion: inputs.aliasFunctionVersion || funcInfo?.Qualifier, + additionalVersions: inputs.additionalVersionWeights, + region: this.region, + aliasName: inputs.aliasName, + description: inputs.aliasDescription, + }); + } + } catch (error) { + const errorType = needCreateAlias ? 'CREATE_ALIAS_SCF' : 'UPDATE_ALIAS_SCF'; + throw new ApiTypeError(errorType, error.message); + } + } else { + // 兼容旧逻辑,即给默认版本$LATEST设置traffic比例的流量,给lastVersion版本设置(1-traffic)比例的流量。 + const needSetTraffic = + inputs.traffic != null && inputs.lastVersion && inputs.lastVersion !== '$LATEST'; + if (needSetTraffic) { await this.alias.update({ namespace, functionName, - functionVersion: inputs.aliasFunctionVersion || funcInfo?.Qualifier, + region: this.region, additionalVersions: needSetTraffic - ? [{ weight: strip(1 - inputs.traffic!), version: aliasAddionalVersion! }] + ? [{ weight: strip(1 - inputs.traffic!), version: inputs.lastVersion! }] : [], - region: this.region, aliasName: inputs.aliasName, description: inputs.aliasDescription, }); + outputs.Traffic = inputs.traffic; + outputs.ConfigTrafficVersion = inputs.lastVersion; } - - outputs.Traffic = inputs.traffic; - outputs.ConfigTrafficVersion = inputs.lastVersion; } // get default alias diff --git a/src/modules/scf/interface.ts b/src/modules/scf/interface.ts index 9825ae3..f86086e 100644 --- a/src/modules/scf/interface.ts +++ b/src/modules/scf/interface.ts @@ -35,6 +35,7 @@ export interface BaseFunctionConfig { Timeout?: number; InitTimeout?: number; MemorySize?: number; + DiskSize?: number; Type?: 'HTTP' | 'Event'; DeployMode?: 'code' | 'image'; PublicNetConfig?: { @@ -69,6 +70,7 @@ export interface BaseFunctionConfig { ProtocolParams?: ProtocolParams; NodeType?: string; NodeSpec?: string; + InstanceConcurrencyConfig?: { DynamicEnabled: 'TRUE' | 'FALSE'; MaxConcurrency?: number }; } export interface TriggerType { @@ -145,9 +147,10 @@ export interface ScfCreateAlias { functionVersion?: string; aliasName: string; namespace?: string; - lastVersion: string; - traffic: number; + lastVersion?: string; + traffic?: number; description?: string; + additionalVersions?: { version: string; weight: number }[]; } export interface ScfCreateFunctionInputs { @@ -167,6 +170,7 @@ export interface ScfCreateFunctionInputs { timeout?: number; initTimeout?: number; memorySize?: number; + diskSize?: number; publicAccess?: boolean; eip?: boolean; l5Enable?: boolean; @@ -245,6 +249,13 @@ export interface ScfCreateFunctionInputs { protocolType?: string; protocolParams?: ProtocolParams; + + // 请求多并发配置 + instanceConcurrencyConfig?: { + enable: boolean; // 是否开启多并发 + dynamicEnabled: boolean; // 是否开启动态配置 + maxConcurrency: number; // 最大并发数 + }; } export interface ScfUpdateAliasTrafficInputs { @@ -270,17 +281,18 @@ export interface ScfDeployInputs extends ScfCreateFunctionInputs { enableRoleAuth?: boolean; region?: string; + // 版本相关配置 lastVersion?: string; publish?: boolean; publishDescription?: string; - needSetTraffic?: boolean; traffic?: number; + // 别名相关配置 aliasName?: string; aliasDescription?: string; aliasFunctionVersion?: string; - aliasAddionalVersion?: string; + additionalVersionWeights?: { version: string; weight: number }[]; tags?: Record; diff --git a/src/modules/scf/utils.ts b/src/modules/scf/utils.ts index a7da9fa..c981250 100644 --- a/src/modules/scf/utils.ts +++ b/src/modules/scf/utils.ts @@ -21,6 +21,7 @@ export const formatInputs = (inputs: ScfCreateFunctionInputs) => { }, L5Enable: inputs.l5Enable === true ? 'TRUE' : 'FALSE', InstallDependency: inputs.installDependency === true ? 'TRUE' : 'FALSE', + DiskSize: +(inputs.diskSize || CONFIGS.defaultDiskSize), }; if (inputs.nodeType) { @@ -96,6 +97,19 @@ export const formatInputs = (inputs: ScfCreateFunctionInputs) => { functionInputs.ProtocolParams = protocolParams; } } + // 仅web函数支持单实例请求多并发,instanceConcurrencyConfig.enable:true,启用多并发;instanceConcurrencyConfig.enable:false,关闭多并发 + if (inputs.instanceConcurrencyConfig) { + if (inputs.instanceConcurrencyConfig.enable) { + functionInputs.InstanceConcurrencyConfig = { + DynamicEnabled: inputs.instanceConcurrencyConfig.dynamicEnabled ? 'TRUE' : 'FALSE', + MaxConcurrency: inputs.instanceConcurrencyConfig.maxConcurrency || 2, + }; + } else { + functionInputs.InstanceConcurrencyConfig = { + DynamicEnabled: '' as any, + }; + } + } } if (inputs.role) { diff --git a/src/utils/index.ts b/src/utils/index.ts index 7677a34..13d5d70 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -31,6 +31,22 @@ export function isArray(obj: T[] | T): obj is T[] { return Object.prototype.toString.call(obj) == '[object Array]'; } +/** + * is positive integer(正整数) + * @param obj object + */ +export function isPositiveInteger(value: string | number) { + return +value > 0 && Number.isInteger(+value); +} + +/** + * is number(数字) + * @param obj object + */ +export function isNumber(value: string | number) { + return !Number.isNaN(+value); +} + /** * is object * @param obj object