Skip to content

feat: 部署函数支持添加云梯默认标签(运营部门,运营产品,负责人) #298

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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
25 changes: 16 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tencent-component-toolkit",
"version": "2.24.2",
"version": "2.24.3",
"description": "Tencent component toolkit",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down Expand Up @@ -66,12 +66,12 @@
"@semantic-release/git": "^9.0.0",
"@semantic-release/npm": "^7.0.4",
"@semantic-release/release-notes-generator": "^9.0.1",
"@types/axios": "^0.14.0",
"@types/react-grid-layout": "^1.1.2",
"@types/uuid": "^8.3.1",
"@typescript-eslint/eslint-plugin": "^4.14.0",
"@typescript-eslint/parser": "^4.14.0",
"@ygkit/secure": "^0.0.3",
"axios": "^0.21.0",
"dotenv": "^8.2.0",
"eslint": "^7.18.0",
"eslint-config-prettier": "^6.10.0",
Expand All @@ -90,6 +90,7 @@
"@types/jest": "^26.0.20",
"@types/node": "^14.14.31",
"@ygkit/request": "^0.1.8",
"axios": "^0.21.0",
"camelcase": "^6.2.0",
"cos-nodejs-sdk-v5": "^2.9.20",
"dayjs": "^1.10.4",
Expand Down
1 change: 1 addition & 0 deletions src/modules/cam/apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const ACTIONS = [
'CreateRole',
'GetRole',
'DeleteRole',
'GetUserAppId',
] as const;

export type ActionType = typeof ACTIONS[number];
Expand Down
52 changes: 52 additions & 0 deletions src/modules/cam/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { ActionType } from './apis';
import { CapiCredentials, RegionType, ApiServiceType } from './../interface';
import { Capi } from '@tencent-sdk/capi';
import APIS from './apis';
import { getYunTiApiUrl } from '../../utils';
import axios from 'axios';

/** CAM (访问管理)for serverless */
export default class Cam {
Expand Down Expand Up @@ -112,4 +114,54 @@ export default class Cam {
async CheckSCFExcuteRole() {
return this.isRoleExist('QCS_SCFExcuteRole');
}

/** 查询用户AppId */
async GetUserAppId(): Promise<{ OwnerUin: string; AppId: string; Uin: string }> {
try {
return this.request({
Action: 'GetUserAppId',
});
} catch (error) {
return {
OwnerUin: '',
AppId: '',
Uin: '',
};
}
}

/**
* checkYunTi 检查是否是云梯账号
* @returns {boolean} true: 是云梯账号; false: 非云梯账号
*/
async checkYunTi(): Promise<boolean> {
let isYunTi = false;
const { OwnerUin: uin } = await this.GetUserAppId();
try {
const params = JSON.stringify({
id: '1',
jsonrpc: '2.0',
method: 'checkOwnUin',
params: { ownUin: [uin] },
});
const apiUrl = getYunTiApiUrl();
const res = await axios.post(apiUrl, params, {
headers: { 'content-type': 'application/json' },
});
if (res?.data?.error?.message) {
throw new Error(res.data.error.message);
} else {
isYunTi =
res?.data?.result?.data &&
res.data.result.data?.some(
(item: { ownUin: string; appId: string }) => item?.ownUin === uin,
);
console.log('check yunTi ownUin:', isYunTi);
}
} catch (error) {
isYunTi = false;
console.log('checkYunTiOwnUin error:', error);
}
return isYunTi;
}
}
1 change: 1 addition & 0 deletions src/modules/scf/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const WebServerImageDefaultPort = 9000;
14 changes: 13 additions & 1 deletion src/modules/scf/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ActionType } from './apis';
import { RegionType, ApiServiceType, CapiCredentials } from './../interface';
import { Capi } from '@tencent-sdk/capi';
import { ApiTypeError } from '../../utils/error';
import { deepClone, strip } from '../../utils';
import { deepClone, formatInputTags, strip } from '../../utils';
import TagsUtils from '../tag/index';
import ApigwUtils from '../apigw';
import CONFIGS from './config';
Expand Down Expand Up @@ -252,13 +252,15 @@ export default class Scf {
credentials: this.credentials,
region: this.region,
});
const tags: any = trigger?.parameters?.tags ?? trigger?.tags ?? funcInfo.Tags;
const triggerOutput = await triggerInstance.create({
scf: this,
region: this.region,
inputs: {
namespace: funcInfo.Namespace,
functionName: funcInfo.FunctionName,
...trigger,
tags: formatInputTags(tags),
},
});

Expand Down Expand Up @@ -457,4 +459,14 @@ export default class Scf {
const logs = await this.scf.getLogs(inputs);
return logs;
}

checkAddedYunTiTags(tags: Array<{ [key: string]: string }>): boolean {
const formatTags = formatInputTags(tags);
const result =
formatTags?.length > 0 &&
['运营部门', '运营产品', '负责人'].every((tagKey) =>
formatTags.some((item) => item.key === tagKey && !!item.value),
);
return result;
}
}
19 changes: 14 additions & 5 deletions src/modules/scf/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export interface FunctionCode {
RegistryId?: string;
Command?: string;
Args?: string;
ContainerImageAccelerate?: boolean;
ImagePort?: number;
};
}

Expand Down Expand Up @@ -76,6 +78,8 @@ export interface TriggerType {
TriggerName?: string;
Qualifier?: string;
compared?: boolean;
tags?: object;
parameters?: any;
}

export type OriginTriggerType = {
Expand Down Expand Up @@ -203,6 +207,7 @@ export interface ScfCreateFunctionInputs {

cfs?: {
cfsId: string;
mountInsId?: string;
MountInsId?: string;
localMountDir: string;
remoteMountDir: string;
Expand All @@ -228,6 +233,10 @@ export interface ScfCreateFunctionInputs {
command?: string;
// 启动命令参数
args?: string;
// 是否开启镜像加速
containerImageAccelerate?: boolean;
// 监听端口: -1 表示job镜像,0~65535 表示Web Server镜像
imagePort?: number;
};

// 异步调用重试配置
Expand Down Expand Up @@ -391,25 +400,25 @@ export interface GetRequestStatusOptions {
/**
* 函数名称
*/
functionName: string
functionName: string;

/**
* 需要查询状态的请求id
*/
functionRequestId: string
functionRequestId: string;

/**
* 函数的所在的命名空间
*/
namespace?: string
namespace?: string;

/**
* 查询的开始时间,例如:2017-05-16 20:00:00,不填默认为当前时间 - 15min
*/
startTime?: string
startTime?: string;

/**
* 查询的结束时间,例如:2017-05-16 20:59:59,不填默认为当前时间。EndTime 需要晚于 StartTime。
*/
endTime?: string
endTime?: string;
}
15 changes: 14 additions & 1 deletion src/modules/scf/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { WebServerImageDefaultPort } from './constants';
import { ScfCreateFunctionInputs, BaseFunctionConfig, ProtocolParams } from './interface';
const CONFIGS = require('./config').default;

Expand Down Expand Up @@ -52,6 +53,18 @@ export const formatInputs = (inputs: ScfCreateFunctionInputs) => {
if (imageConfig.args) {
functionInputs.Code!.ImageConfig!.Args = imageConfig.args;
}
// 镜像加速
if (imageConfig.containerImageAccelerate !== undefined) {
functionInputs.Code!.ImageConfig!.ContainerImageAccelerate =
imageConfig.containerImageAccelerate;
}
// 监听端口: -1 表示 job镜像,0 ~ 65526 表示webServer镜像
if (imageConfig.imagePort) {
functionInputs.Code!.ImageConfig!.ImagePort =
Number.isInteger(imageConfig?.imagePort) && imageConfig?.imagePort === -1
? -1
: WebServerImageDefaultPort;
}
} else {
// 基于 COS 代码部署
functionInputs.Code = {
Expand Down Expand Up @@ -149,7 +162,7 @@ export const formatInputs = (inputs: ScfCreateFunctionInputs) => {
inputs.cfs.forEach((item) => {
functionInputs.CfsConfig?.CfsInsList.push({
CfsId: item.cfsId,
MountInsId: item.MountInsId || item.cfsId,
MountInsId: item.mountInsId || item.MountInsId || item.cfsId,
LocalMountDir: item.localMountDir,
RemoteMountDir: item.remoteMountDir,
UserGroupId: String(item.userGroupId || 10000),
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 @@ -147,7 +147,7 @@ export default class ApigwTrigger extends BaseTrigger<ApigwTriggerInputsParams>
funcInfo?: FunctionInfo;
inputs: TriggerInputs<ApigwTriggerInputsParams>;
}) {
const { parameters, isAutoRelease } = inputs;
const { parameters, isAutoRelease, tags } = inputs;
const {
oldState,
protocols,
Expand Down Expand Up @@ -192,6 +192,7 @@ export default class ApigwTrigger extends BaseTrigger<ApigwTriggerInputsParams>
method: endpoints[0].method ?? 'ANY',
},
created: !!parameters?.created,
tags,
};
const triggerKey = this.getKey(triggerInputs);

Expand Down
4 changes: 4 additions & 0 deletions src/modules/triggers/interface/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ApigwDeployInputs, ApiEndpoint } from '../../apigw/interface';
import { TagInput } from '../../interface';

export interface ApigwTriggerRemoveScfTriggerInputs {
serviceId: string;
Expand Down Expand Up @@ -129,6 +130,9 @@ export interface TriggerInputs<P extends TriggerInputsParams = TriggerInputsPara

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

// 标签列表
tags?: TagInput[];
}

export interface TriggerDetail {
Expand Down
52 changes: 52 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import path from 'path';
import camelCase from 'camelcase';
import { PascalCase } from 'type-fest';
import { CamelCasedProps, PascalCasedProps } from '../modules/interface';
import crypto from 'crypto';

// TODO: 将一些库换成 lodash

Expand Down Expand Up @@ -273,3 +274,54 @@ export const getQcsResourceId = (service: string, region: string, uin: string, s
// 云资源六段式
return `qcs::${service}:${region}:uin/${uin}:${suffix}`;
};

/**
* hmacSha1 加密HmacSHA1
* @param text 加密文本
* @param key 加密密钥
* @returns
*/
export const hmacSha1 = (text: string, key: string) => {
return crypto.createHmac('sha1', key).update(text).digest('hex');
};

/**
* getYunTiApiUrl 查询云梯API地址
* @returns 云梯API地址
*/
export const getYunTiApiUrl = (): string => {
const apiKey = process.env.SLS_YUNTI_API_KEY || '';
const apiSecret = process.env.SLS_YUNTI_API_SECRET || '';
const apiUrl = process.env.SLS_YUNTI_API_URL;
const timeStamp = Math.floor(Date.now() / 1000);
const apiSign = hmacSha1(`${timeStamp}${apiKey}`, apiSecret);
const url = `${apiUrl}?api_key=${apiKey}&api_ts=${timeStamp}&api_sign=${apiSign}`;
return url;
};

/**
* formatInputTags 格式化输入标签
*/
export const formatInputTags = (
input: Array<any> | { [key: string]: string },
): { key: string; value: string }[] => {
let tags: { key: string; value: string }[];
if (Array.isArray(input)) {
tags = input.map((item) => {
return {
key: item?.key ?? item?.Key ?? '',
value: item?.value ?? item?.Value ?? '',
};
});
} else if (typeof input === 'object' && input) {
tags = Object.entries(input).map(([key, value]) => {
return {
key: (key ?? '').toString(),
value: (value ?? '').toString(),
};
});
} else {
tags = undefined as any;
}
return tags;
};