From b525006b61263a20fbbabe3c10a6949541354427 Mon Sep 17 00:00:00 2001 From: brycewwang <865689594@qq.com> Date: Tue, 23 Apr 2024 14:29:24 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20yml=E6=94=AF=E6=8C=81job=E9=95=9C?= =?UTF-8?q?=E5=83=8F=E9=85=8D=E7=BD=AE&=E4=BF=AE=E5=A4=8Dcfs=E6=89=BE?= =?UTF-8?q?=E4=B8=8D=E5=88=B0=E6=8C=82=E8=BD=BD=E7=82=B9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/scf/constants.ts | 1 + src/modules/scf/interface.ts | 17 ++++++++++++----- src/modules/scf/utils.ts | 17 ++++++++++++++++- 3 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 src/modules/scf/constants.ts diff --git a/src/modules/scf/constants.ts b/src/modules/scf/constants.ts new file mode 100644 index 0000000..bc6249f --- /dev/null +++ b/src/modules/scf/constants.ts @@ -0,0 +1 @@ +export const WebServerImageDefaultPort = 9000; diff --git a/src/modules/scf/interface.ts b/src/modules/scf/interface.ts index d7f3d6b..f5da63a 100644 --- a/src/modules/scf/interface.ts +++ b/src/modules/scf/interface.ts @@ -12,6 +12,8 @@ export interface FunctionCode { RegistryId?: string; Command?: string; Args?: string; + ContainerImageAccelerate?: boolean; + ImagePort?: number; }; } @@ -203,6 +205,7 @@ export interface ScfCreateFunctionInputs { cfs?: { cfsId: string; + mountInsId?: string; MountInsId?: string; localMountDir: string; remoteMountDir: string; @@ -228,6 +231,10 @@ export interface ScfCreateFunctionInputs { command?: string; // 启动命令参数 args?: string; + // 是否开启镜像加速 + containerImageAccelerate?: boolean; + // 监听端口: -1 表示job镜像,0~65535 表示Web Server镜像 + imagePort?: number; }; // 异步调用重试配置 @@ -391,25 +398,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; } diff --git a/src/modules/scf/utils.ts b/src/modules/scf/utils.ts index 0c905d1..684f95d 100644 --- a/src/modules/scf/utils.ts +++ b/src/modules/scf/utils.ts @@ -1,3 +1,4 @@ +import { WebServerImageDefaultPort } from './constants'; import { ScfCreateFunctionInputs, BaseFunctionConfig, ProtocolParams } from './interface'; const CONFIGS = require('./config').default; @@ -52,6 +53,20 @@ 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 && + imageConfig?.imagePort <= 65535 + ? imageConfig.imagePort + : WebServerImageDefaultPort; + } } else { // 基于 COS 代码部署 functionInputs.Code = { @@ -149,7 +164,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), From 0091a093f8319cf2e6291e5610db114627a05f9d Mon Sep 17 00:00:00 2001 From: brycewwang <865689594@qq.com> Date: Tue, 23 Apr 2024 14:32:18 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=E9=83=A8=E7=BD=B2=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E6=94=AF=E6=8C=81=E6=B7=BB=E5=8A=A0=E4=BA=91=E6=A2=AF?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E6=A0=87=E7=AD=BE(=E8=BF=90=E8=90=A5?= =?UTF-8?q?=E9=83=A8=E9=97=A8,=E8=BF=90=E8=90=A5=E4=BA=A7=E5=93=81,?= =?UTF-8?q?=E8=B4=9F=E8=B4=A3=E4=BA=BA)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 25 +++++++----- package.json | 5 ++- src/modules/cam/apis.ts | 1 + src/modules/cam/index.ts | 52 +++++++++++++++++++++++++ src/modules/scf/index.ts | 14 ++++++- src/modules/scf/interface.ts | 2 + src/modules/scf/utils.ts | 6 +-- src/modules/triggers/apigw.ts | 3 +- src/modules/triggers/interface/index.ts | 4 ++ src/utils/index.ts | 52 +++++++++++++++++++++++++ 10 files changed, 147 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index b905b76..356cc67 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3887,6 +3887,15 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, + "@types/axios": { + "version": "0.14.0", + "resolved": "https://mirrors.tencent.com/npm/@types/axios/-/axios-0.14.0.tgz", + "integrity": "sha512-KqQnQbdYE54D7oa/UmYVMZKq7CO4l8DEENzOKc4aBRwxCXSlJXGz83flFx5L7AWrOQnmuN3kVsRdt+GZPPjiVQ==", + "dev": true, + "requires": { + "axios": "*" + } + }, "@types/babel__core": { "version": "7.1.15", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.15.tgz", @@ -4673,12 +4682,11 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dev": true, + "version": "0.21.0", + "resolved": "https://mirrors.tencent.com/npm/axios/-/axios-0.21.0.tgz", + "integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==", "requires": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.10.0" } }, "babel-jest": { @@ -7374,10 +7382,9 @@ "dev": true }, "follow-redirects": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.3.tgz", - "integrity": "sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw==", - "dev": true + "version": "1.15.6", + "resolved": "https://mirrors.tencent.com/npm/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" }, "for-in": { "version": "1.0.2", diff --git a/package.json b/package.json index 2faee77..96ef2b6 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", @@ -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", diff --git a/src/modules/cam/apis.ts b/src/modules/cam/apis.ts index 99543d6..e3e5ee2 100644 --- a/src/modules/cam/apis.ts +++ b/src/modules/cam/apis.ts @@ -8,6 +8,7 @@ const ACTIONS = [ 'CreateRole', 'GetRole', 'DeleteRole', + 'GetUserAppId', ] as const; export type ActionType = typeof ACTIONS[number]; diff --git a/src/modules/cam/index.ts b/src/modules/cam/index.ts index f98e5d0..db2b87e 100644 --- a/src/modules/cam/index.ts +++ b/src/modules/cam/index.ts @@ -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 { @@ -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 { + 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; + } } diff --git a/src/modules/scf/index.ts b/src/modules/scf/index.ts index e8ebd76..2813f2a 100644 --- a/src/modules/scf/index.ts +++ b/src/modules/scf/index.ts @@ -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'; @@ -252,6 +252,7 @@ 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, @@ -259,6 +260,7 @@ export default class Scf { namespace: funcInfo.Namespace, functionName: funcInfo.FunctionName, ...trigger, + tags: formatInputTags(tags), }, }); @@ -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; + } } diff --git a/src/modules/scf/interface.ts b/src/modules/scf/interface.ts index f5da63a..f714a7b 100644 --- a/src/modules/scf/interface.ts +++ b/src/modules/scf/interface.ts @@ -78,6 +78,8 @@ export interface TriggerType { TriggerName?: string; Qualifier?: string; compared?: boolean; + tags?: object; + parameters?: any; } export type OriginTriggerType = { diff --git a/src/modules/scf/utils.ts b/src/modules/scf/utils.ts index 684f95d..a7da9fa 100644 --- a/src/modules/scf/utils.ts +++ b/src/modules/scf/utils.ts @@ -61,10 +61,8 @@ export const formatInputs = (inputs: ScfCreateFunctionInputs) => { // 监听端口: -1 表示 job镜像,0 ~ 65526 表示webServer镜像 if (imageConfig.imagePort) { functionInputs.Code!.ImageConfig!.ImagePort = - Number.isInteger(imageConfig?.imagePort) && - imageConfig?.imagePort >= -1 && - imageConfig?.imagePort <= 65535 - ? imageConfig.imagePort + Number.isInteger(imageConfig?.imagePort) && imageConfig?.imagePort === -1 + ? -1 : WebServerImageDefaultPort; } } else { diff --git a/src/modules/triggers/apigw.ts b/src/modules/triggers/apigw.ts index 5438d62..cfaba5f 100644 --- a/src/modules/triggers/apigw.ts +++ b/src/modules/triggers/apigw.ts @@ -147,7 +147,7 @@ export default class ApigwTrigger extends BaseTrigger funcInfo?: FunctionInfo; inputs: TriggerInputs; }) { - const { parameters, isAutoRelease } = inputs; + const { parameters, isAutoRelease, tags } = inputs; const { oldState, protocols, @@ -192,6 +192,7 @@ export default class ApigwTrigger extends BaseTrigger method: endpoints[0].method ?? 'ANY', }, created: !!parameters?.created, + tags, }; const triggerKey = this.getKey(triggerInputs); diff --git a/src/modules/triggers/interface/index.ts b/src/modules/triggers/interface/index.ts index ca07f7d..0b7c65d 100644 --- a/src/modules/triggers/interface/index.ts +++ b/src/modules/triggers/interface/index.ts @@ -1,4 +1,5 @@ import { ApigwDeployInputs, ApiEndpoint } from '../../apigw/interface'; +import { TagInput } from '../../interface'; export interface ApigwTriggerRemoveScfTriggerInputs { serviceId: string; @@ -129,6 +130,9 @@ export interface TriggerInputs

{ + 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 | { [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; +};