Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion __tests__/asw.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { sleep } from '@ygkit/request';
import Asw from '../src/modules/asw';
import { UpdateOptions, CreateResult } from './../src/modules/asw/interface';

describe('Account', () => {
describe('Asw', () => {
const credentials = {
SecretId: process.env.TENCENT_SECRET_ID,
SecretKey: process.env.TENCENT_SECRET_KEY,
Expand Down
4 changes: 2 additions & 2 deletions __tests__/trigger.manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ describe('Trigger Manager', () => {
];

test('bulk create triggers', async () => {
const res = await client.bulkCreateTriggers(triggers);
const { triggerList } = await client.bulkCreateTriggers(triggers);

expect(res).toEqual([
expect(triggerList).toEqual([
{
name: functionConfig.name,
triggers: [
Expand Down
25 changes: 20 additions & 5 deletions src/modules/apigw/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@ export default class Apigw {

/** 部署 API 网关 */
async deploy(inputs: ApigwDeployInputs) {
const { environment = 'release' as const, oldState = {}, isInputServiceId = false } = inputs;
const {
environment = 'release' as const,
oldState = {},
isInputServiceId = false,
isAutoRelease = false,
} = inputs;
if (isInputServiceId) {
return this.deployWIthInputServiceId(inputs as ApigwDeployWithServiceIdInputs);
}
Expand All @@ -107,7 +112,9 @@ export default class Apigw {
environment,
});

await this.service.release({ serviceId, environment });
if (!isAutoRelease) {
await this.service.release({ serviceId, environment });
}

console.log(`Deploy service ${serviceId} success`);

Expand Down Expand Up @@ -162,6 +169,7 @@ export default class Apigw {
customDomains,
usagePlan,
isRemoveTrigger = false,
isAutoRelease = true,
} = inputs;

// check service exist
Expand All @@ -180,7 +188,7 @@ export default class Apigw {

// 定制化需求:如果用户在yaml中配置了 serviceId,则只执行删除 api 逻辑
// 删除后需要重新发布
if (isRemoveTrigger) {
if (isRemoveTrigger && isAutoRelease) {
await this.service.release({ serviceId, environment });
return;
}
Expand Down Expand Up @@ -240,7 +248,12 @@ export default class Apigw {
}

async deployWIthInputServiceId(inputs: ApigwDeployWithServiceIdInputs) {
const { environment = 'release' as const, oldState = {}, serviceId } = inputs;
const {
environment = 'release' as const,
oldState = {},
serviceId,
isAutoRelease = true,
} = inputs;
inputs.protocols = getProtocolString(inputs.protocols as ('http' | 'https')[]);

const endpoints = inputs.endpoints || [];
Expand All @@ -255,7 +268,9 @@ export default class Apigw {
environment,
});

await this.service.release({ serviceId, environment });
if (!isAutoRelease) {
await this.service.release({ serviceId, environment });
}

console.log(`Deploy service ${serviceId} success`);

Expand Down
4 changes: 4 additions & 0 deletions src/modules/apigw/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ export interface ApigwDeployInputs extends ApigwCreateServiceInputs, ApigwBindCu
endpoints?: ApiEndpoint[];
isInputServiceId?: boolean;
isRemoveTrigger?: boolean;

// 是否自动发布服务(API 网关特有)
isAutoRelease?: boolean;
}

export type ApigwDeployWithServiceIdInputs = ApigwDeployInputs & { serviceId: string };
Expand Down Expand Up @@ -253,6 +256,7 @@ export interface ApigwRemoveInputs {
usagePlan?: ApigwSetupUsagePlanInputs;
isInputServiceId?: boolean;
isRemoveTrigger?: boolean;
isAutoRelease?: boolean;
}

export interface ApiDetail {
Expand Down
1 change: 1 addition & 0 deletions src/modules/scf/entities/scf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ export default class ScfEntity extends BaseEntity {
// 更新函数接口不能传递以下参数
delete reqInputs.Type;
delete reqInputs.Handler;
delete reqInputs.Runtime;
delete reqInputs.Code;
delete reqInputs.AsyncRunEnable;
delete reqInputs.InstallDependency;
Expand Down
3 changes: 2 additions & 1 deletion src/modules/scf/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ export default class Scf {
await this.scf.isOperational({ namespace, functionName });
} catch (e) {}

const { isAutoRelease = true } = inputs;
const triggers = inputs.Triggers || inputs.triggers;
if (triggers) {
for (let i = 0; i < triggers.length; i++) {
Expand All @@ -378,6 +379,7 @@ export default class Scf {
// delete apigw trigger
const curTrigger = triggers[i];
curTrigger.isRemoveTrigger = true;
curTrigger.isAutoRelease = isAutoRelease;
await this.apigwClient.remove(curTrigger as ApigwRemoveInputs);
} catch (e) {
console.log(e);
Expand All @@ -387,7 +389,6 @@ export default class Scf {
}

await this.scf.delete({ namespace, functionName });

console.log(`Remove function ${functionName} success`);

return true;
Expand Down
3 changes: 3 additions & 0 deletions src/modules/scf/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,9 @@ export interface ScfRemoveInputs {

Triggers?: ApigwRemoveInputs[] | Record<string, any>[];
triggers?: ApigwRemoveInputs[] | Record<string, any>[];

// 是否自动发布 API 网关
isAutoRelease?: boolean;
}

export interface ScfInvokeInputs {
Expand Down
3 changes: 2 additions & 1 deletion src/modules/triggers/apigw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export default class ApigwTrigger extends BaseTrigger<ApigwTriggerInputsParams>
funcInfo?: FunctionInfo;
inputs: TriggerInputs<ApigwTriggerInputsParams>;
}) {
const { parameters } = inputs;
const { parameters, isAutoRelease } = inputs;
const {
oldState,
protocols,
Expand All @@ -158,6 +158,7 @@ export default class ApigwTrigger extends BaseTrigger<ApigwTriggerInputsParams>
} = parameters!;
const endpoints = parameters?.endpoints ?? [{ path: '/', method: 'ANY' }];
const triggerInputs: ApigwTriggerInputsParams = {
isAutoRelease,
oldState: oldState ?? {},
region,
protocols,
Expand Down
12 changes: 12 additions & 0 deletions src/modules/triggers/interface/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ export interface TriggerInputs<P extends TriggerInputsParams = TriggerInputsPara
FunctionName?: string;
Namespace?: string;
Qualifier?: string;

// 是否自动发布服务(API 网关特有)
isAutoRelease?: boolean;
}

export interface TriggerDetail {
Expand All @@ -137,6 +140,8 @@ export interface TriggerDetail {
compared?: boolean;

triggerType: string;

[key: string]: any;
}

export interface NewTriggerInputs {
Expand All @@ -155,3 +160,10 @@ export interface NewTriggerInputs {
}

export * from './clb';

export interface SimpleApigwDetail {
functionName: string;
serviceId: string;
serviceName: string;
environment: string;
}
74 changes: 56 additions & 18 deletions src/modules/triggers/manager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SimpleApigwDetail } from './interface/index';
import { Capi } from '@tencent-sdk/capi';
import { sleep } from '@ygkit/request';
import { ActionType } from '../scf/apis';
import { RegionType, ApiServiceType, CapiCredentials } from '../interface';
import { ApiError } from '../../utils/error';
Expand Down Expand Up @@ -105,7 +105,6 @@ export class TriggerManager {
const deleteList: (TriggerDetail | null)[] = deepClone(oldList);
const createList: (NewTriggerInputs | null)[] = deepClone(events);
const deployList: (TriggerDetail | null)[] = [];
// const noKeyTypes = ['apigw'];
const updateList: (NewTriggerInputs | null)[] = [];

for (let index = 0; index < events.length; index++) {
Expand Down Expand Up @@ -175,6 +174,7 @@ export class TriggerManager {
};
}

// 删除函数触发器
async removeTrigger({
trigger,
name,
Expand Down Expand Up @@ -209,7 +209,7 @@ export class TriggerManager {
}

// 部署函数触发器
async deployTrigger({
async createTrigger({
name,
namespace = 'default',
events = [],
Expand Down Expand Up @@ -242,6 +242,7 @@ export class TriggerManager {
}

// 2. 创建新的触发器
const apigwServiceList: SimpleApigwDetail[] = [];
for (let i = 0; i < deployList.length; i++) {
const trigger = deployList[i];
const { Type } = trigger;
Expand All @@ -257,21 +258,27 @@ export class TriggerManager {
credentials: this.credentials,
region: this.region,
});
// 针对触发器创建接口限频,由于后端服务问题,必须设置并发为 1
// TODO: 兼容多个网关触发器并行部署时,服务发布会报错,待后端接口支持状态查询后再额外改造 apigw 模块
this.runningTasks++;
if (this.runningTasks > this.maxRunningTasks) {
await sleep(1000);
}

const triggerOutput = await triggerInstance.create({
scf: this,
region: this.region,
inputs: {
namespace,
functionName: name,
// 禁用自动发布
isAutoRelease: false,
...trigger,
},
});
// 筛选出 API 网关触发器,可以单独的进行发布
if (triggerOutput.serviceId) {
apigwServiceList.push({
functionName: name,
serviceId: triggerOutput.serviceId,
serviceName: triggerOutput.serviceName,
environment: triggerOutput.environment,
});
}
this.runningTasks--;

deployList[i] = {
Expand All @@ -290,7 +297,7 @@ export class TriggerManager {
name,
triggers: deployList,
};
return outputs;
return { outputs, apigwServiceList };
}

/**
Expand Down Expand Up @@ -396,6 +403,32 @@ export class TriggerManager {
});
}

/**
* 批量发布 API 网关,防止重复发布同一个网关
* @param list API 网关列表
*/
async bulkReleaseApigw(list: SimpleApigwDetail[]) {
// 筛选非重复的网关服务
const uniqueList: SimpleApigwDetail[] = [];
const map: { [key: string]: number } = {};
list.forEach((item) => {
if (!map[item.serviceId]) {
map[item.serviceId] = 1;
uniqueList.push(item);
}
});

const releaseTask: Promise<any>[] = [];
for (let i = 0; i < uniqueList.length; i++) {
const temp = uniqueList[i];
const exist = await this.apigwClient.service.getById(temp.serviceId);
if (exist) {
releaseTask.push(this.apigwClient.service.release(temp));
}
}
await Promise.all(releaseTask);
}

/**
* 批量处理多函数关联的触发器配置
* @param triggers 触发器列表
Expand All @@ -404,28 +437,33 @@ export class TriggerManager {
async bulkCreateTriggers(triggers: NewTriggerInputs[] = []) {
const scfList = await this.getScfsByTriggers(triggers);

const createTasks: Promise<any>[] = [];
let apigwList: SimpleApigwDetail[] = [];
const res = [];
for (let i = 0; i < scfList.length; i++) {
const curScf = scfList[i];
const triggersConfig = this.getScfTriggersConfig({
name: curScf.name,
triggers,
});
const task = async () => {
const res = await this.deployTrigger({
const { outputs, apigwServiceList } = await this.createTrigger({
name: curScf.name,
namespace: curScf.namespace,
events: triggersConfig,
});
// this.runningTasks--;
return res;
apigwList = apigwList.concat(apigwServiceList);
return outputs;
};

createTasks.push(task());
const temp = await task();
res.push(temp);
}
const res = await Promise.all(createTasks);

return res;
await this.bulkReleaseApigw(apigwList);

return {
triggerList: res,
apigwList,
};
}

/**
Expand Down