From 5e25d520127dfc4f61f850f4bb367cf0a95e7712 Mon Sep 17 00:00:00 2001 From: seven Date: Fri, 21 Feb 2025 22:58:37 +0800 Subject: [PATCH 1/4] feat: add container to fc schema Signed-off-by: seven --- samples/aliyun-poc-fc-gpu.yml | 4 -- src/parser/functionParser.ts | 5 +- src/stack/rfsStack/function.ts | 6 +- src/stack/rosStack/function.ts | 62 +++++++++++++++----- src/types/domains/function.ts | 28 +++++++-- src/validator/functionSchema.ts | 60 ++++++++++++------- tests/fixtures/deployFixture.ts | 36 ++++++++---- tests/fixtures/serverless-insight-huawei.yml | 7 ++- tests/fixtures/serverless-insight.yml | 7 ++- tests/stack/deploy.test.ts | 2 +- 10 files changed, 144 insertions(+), 73 deletions(-) diff --git a/samples/aliyun-poc-fc-gpu.yml b/samples/aliyun-poc-fc-gpu.yml index a19a7d0..e481f94 100644 --- a/samples/aliyun-poc-fc-gpu.yml +++ b/samples/aliyun-poc-fc-gpu.yml @@ -23,10 +23,6 @@ tags: functions: insight_poc_fn: name: insight-poc-gpu-fns - code: - runtime: nodejs18 - handler: ${vars.handler} - path: tests/fixtures/artifacts/artifact.zip container: image: registry.cn-hangzhou.aliyuncs.com/aliyunfc/runtime/nodejs18:1.8.0 command: [node, index.handler] diff --git a/src/parser/functionParser.ts b/src/parser/functionParser.ts index 82064f1..c23014a 100644 --- a/src/parser/functionParser.ts +++ b/src/parser/functionParser.ts @@ -10,12 +10,11 @@ export const parseFunction = (functions?: { return Object.entries(functions).map(([key, func]) => ({ key, name: func.name, - runtime: func.runtime, - handler: func.handler, + code: func.code, + container: func.container, memory: func.memory, timeout: func.timeout, environment: func.environment, - code: func.code, log: func.log, network: func.network, storage: { diff --git a/src/stack/rfsStack/function.ts b/src/stack/rfsStack/function.ts index 44aa246..3629af4 100644 --- a/src/stack/rfsStack/function.ts +++ b/src/stack/rfsStack/function.ts @@ -13,13 +13,13 @@ resource "huaweicloud_fgs_application" "${service}_app" { const fgsFunction = (fn: FunctionDomain, context: ActionContext, service: string) => ` resource "huaweicloud_fgs_function" "${fn.key}" { name = "${fn.name}" - handler = "${fn.handler}" - runtime = "${fn.runtime}" + handler = "${fn.code!.handler}" + runtime = "${fn.code!.runtime}" memory_size = ${fn.memory} timeout = ${fn.timeout} environment = ${JSON.stringify(fn.environment)} code_type = "inline" - func_code = "${resolveCode(fn.code)}" + func_code = "${resolveCode(fn.code!.path)}" app = "huaweicloud_fgs_application.${service}_app.id" } `; diff --git a/src/stack/rosStack/function.ts b/src/stack/rosStack/function.ts index 73331d8..1d450d7 100644 --- a/src/stack/rosStack/function.ts +++ b/src/stack/rosStack/function.ts @@ -14,7 +14,7 @@ import * as ros from '@alicloud/ros-cdk-core'; import * as sls from '@alicloud/ros-cdk-sls'; import * as nas from '@alicloud/ros-cdk-nas'; import * as ecs from '@alicloud/ros-cdk-ecs'; -import { RosFunction } from '@alicloud/ros-cdk-fc3/lib/fc3.generated'; +import { RosFunction, RosFunctionProps } from '@alicloud/ros-cdk-fc3/lib/fc3.generated'; const storageClassMap = { [NasStorageClassEnum.STANDARD_CAPACITY]: { fileSystemType: 'standard', storageType: 'Capacity' }, @@ -99,11 +99,11 @@ export const resolveFunctions = ( } const fileSources = functions - ?.filter(({ code }) => readCodeSize(code) > CODE_ZIP_SIZE_LIMIT) + ?.filter(({ code }) => code?.path && readCodeSize(code.path) > CODE_ZIP_SIZE_LIMIT) .map(({ code, name }) => { const fcName = replaceReference(name, context); - return { fcName, ...getFileSource(fcName, code) }; + return { fcName, ...getFileSource(fcName, code!.path) }; }); const destinationBucketName = ros.Fn.sub( @@ -125,17 +125,49 @@ export const resolveFunctions = ( true, ); } + functions?.forEach((fnc) => { - const storeInBucket = readCodeSize(fnc.code) > CODE_ZIP_SIZE_LIMIT; - let code: fc.RosFunction.CodeProperty = { - zipFile: resolveCode(fnc.code), - }; - if (storeInBucket) { - code = { - ossBucketName: destinationBucketName, - ossObjectName: fileSources?.find( - ({ fcName }) => fcName === replaceReference(fnc.name, context), - )?.objectKey, + let runtimeConfig: + | { + customContainerConfig: RosFunctionProps['customContainerConfig']; + runtime: RosFunctionProps['runtime']; + handler: RosFunctionProps['handler']; + } + | { + code: RosFunctionProps['code']; + runtime: RosFunctionProps['runtime']; + handler: RosFunctionProps['handler']; + }; + + const storeInBucket = !!fnc.code?.path && readCodeSize(fnc.code.path) > CODE_ZIP_SIZE_LIMIT; + + if (fnc.container) { + runtimeConfig = { + runtime: 'custom-container', + handler: '', + customContainerConfig: { + image: fnc.container.image, + command: fnc.container.command, + entrypoint: fnc.container.entrypoint, + port: fnc.container.port, + }, + }; + } else { + let code: fc.RosFunction.CodeProperty = { + zipFile: resolveCode(fnc.code!.path), + }; + if (storeInBucket) { + code = { + ossBucketName: destinationBucketName, + ossObjectName: fileSources?.find( + ({ fcName }) => fcName === replaceReference(fnc.name, context), + )?.objectKey, + }; + } + runtimeConfig = { + code, + handler: replaceReference(fnc.code!.handler, context), + runtime: replaceReference(fnc.code!.runtime, context), }; } @@ -216,15 +248,13 @@ export const resolveFunctions = ( fnc.key, { functionName: replaceReference(fnc.name, context), - handler: replaceReference(fnc.handler, context), - runtime: replaceReference(fnc.runtime, context), memorySize: replaceReference(fnc.memory, context), timeout: replaceReference(fnc.timeout, context), diskSize: fnc.storage?.disk, environmentVariables: replaceReference(fnc.environment, context), - code, logConfig, vpcConfig, + ...runtimeConfig, nasConfig: fcNas?.length ? { mountPoints: fcNas?.map(({ nasMount, mountDir }) => ({ diff --git a/src/types/domains/function.ts b/src/types/domains/function.ts index 0ac0492..8c46511 100644 --- a/src/types/domains/function.ts +++ b/src/types/domains/function.ts @@ -1,8 +1,16 @@ export type FunctionRaw = { name: string; - runtime: string; - handler: string; - code: string; + code?: { + runtime: string; + handler: string; + path: string; + }; + container?: { + image: string; + command: Array; + entrypoint: Array; + port: number; + }; memory: number; timeout: number; log?: boolean; @@ -30,9 +38,17 @@ export type FunctionRaw = { export type FunctionDomain = { key: string; name: string; - runtime: string; - handler: string; - code: string; + code?: { + runtime: string; + handler: string; + path: string; + }; + container?: { + image: string; + command: Array; + entrypoint: Array; + port: number; + }; memory: number; timeout: number; log?: boolean; diff --git a/src/validator/functionSchema.ts b/src/validator/functionSchema.ts index 8c1d10a..2fca119 100644 --- a/src/validator/functionSchema.ts +++ b/src/validator/functionSchema.ts @@ -4,30 +4,48 @@ export const functionSchema = { patternProperties: { '.*': { type: 'object', - required: ['name', 'runtime', 'handler', 'code'], + required: ['name'], properties: { name: { type: 'string' }, - runtime: { - type: 'string', - enum: [ - 'nodejs20', - 'nodejs18', - 'nodejs16', - 'nodejs14', - 'nodejs12', - 'nodejs10', - 'nodejs8', - 'python3.10', - 'python3.9', - 'python3', - 'PHP 7.2', - 'Java 11', - '.NET Core 3.1', - 'Go 1.x', - ], + code: { + type: 'object', + required: ['runtime', 'handler', 'path'], + additionalProperties: false, + properties: { + runtime: { + type: 'string', + enum: [ + 'nodejs20', + 'nodejs18', + 'nodejs16', + 'nodejs14', + 'nodejs12', + 'nodejs10', + 'nodejs8', + 'python3.10', + 'python3.9', + 'python3', + 'PHP 7.2', + 'Java 11', + '.NET Core 3.1', + 'Go 1.x', + ], + }, + handler: { type: 'string' }, + path: { type: 'string' }, + }, + }, + container: { + type: 'object', + required: ['image', 'command', 'entrypoint', 'port'], + additionalProperties: false, + properties: { + image: { type: 'string' }, + command: { type: 'array', items: { type: 'string' } }, + entrypoint: { type: 'array', items: { type: 'string' } }, + port: { type: 'number' }, + }, }, - handler: { type: 'string' }, - code: { type: 'string' }, memory: { type: 'number' }, timeout: { type: 'number' }, log: { type: 'boolean' }, diff --git a/tests/fixtures/deployFixture.ts b/tests/fixtures/deployFixture.ts index adac1e7..228f426 100644 --- a/tests/fixtures/deployFixture.ts +++ b/tests/fixtures/deployFixture.ts @@ -7,7 +7,7 @@ export const oneFcOneGatewayIac = { version: '0.0.1', provider: { name: 'aliyun' as ProviderEnum, - region: "cn-hangzhou'", + region: 'cn-hangzhou', }, vars: { account_id: 1234567890, @@ -22,9 +22,11 @@ export const oneFcOneGatewayIac = { { key: 'hello_fn', name: 'hello_fn', - runtime: 'nodejs18', - handler: 'index.handler', - code: 'tests/fixtures/artifacts/artifact.zip', + code: { + runtime: 'nodejs18', + handler: 'index.handler', + path: 'tests/fixtures/artifacts/artifact.zip', + }, memory: 128, timeout: 10, log: true, @@ -255,6 +257,7 @@ export const oneFcOneGatewayRos = { }, }, }; + export const referredServiceIac = set( cloneDeep(oneFcOneGatewayIac), 'service', @@ -462,9 +465,11 @@ export const minimumIac = { { key: 'hello_fn', name: 'hello_fn', - runtime: 'nodejs18', - handler: 'index.handler', - code: 'tests/fixtures/artifacts/artifact.zip', + code: { + runtime: 'nodejs18', + handler: 'index.handler', + path: 'tests/fixtures/artifacts/artifact.zip', + }, }, ], } as ServerlessIac; @@ -544,9 +549,11 @@ export const oneFcIac = { { key: 'hello_fn', name: 'hello_fn', - runtime: 'nodejs18', - handler: 'index.handler', - code: 'tests/fixtures/artifacts/artifact.zip', + code: { + runtime: 'nodejs18', + handler: 'index.handler', + path: 'tests/fixtures/artifacts/artifact.zip', + }, memory: 128, timeout: 10, log: true, @@ -688,6 +695,7 @@ export const oneFcIacWithNas = { }, ], } as ServerlessIac; + export const oneFcIacWithNasRos = { ...oneFcRos, Resources: { @@ -838,9 +846,11 @@ export const oneFcIacWithStage = { { key: 'hello_fn', name: 'hello_fn', - runtime: 'nodejs18', - handler: 'index.handler', - code: 'tests/fixtures/artifacts/artifact.zip', + code: { + runtime: 'nodejs18', + handler: 'index.handler', + path: 'tests/fixtures/artifacts/artifact.zip', + }, memory: 128, timeout: 10, log: true, diff --git a/tests/fixtures/serverless-insight-huawei.yml b/tests/fixtures/serverless-insight-huawei.yml index fc4c596..3e85785 100644 --- a/tests/fixtures/serverless-insight-huawei.yml +++ b/tests/fixtures/serverless-insight-huawei.yml @@ -23,9 +23,10 @@ tags: functions: insight_poc_fn: name: insight-poc-fn - runtime: nodejs18 - handler: ${vars.handler} - code: tests/fixtures/artifacts/artifact.zip + code: + runtime: nodejs18 + handler: ${vars.handler} + path: tests/fixtures/artifacts/artifact.zip memory: 512 timeout: 10 environment: diff --git a/tests/fixtures/serverless-insight.yml b/tests/fixtures/serverless-insight.yml index 551df60..a7c1179 100644 --- a/tests/fixtures/serverless-insight.yml +++ b/tests/fixtures/serverless-insight.yml @@ -26,9 +26,10 @@ tags: functions: insight_poc_fn: name: insight-poc-fn - runtime: nodejs18 - handler: ${vars.handler} - code: tests/fixtures/artifacts/artifact.zip + code: + runtime: nodejs18 + handler: ${vars.handler} + path: tests/fixtures/artifacts/artifact.zip memory: 512 timeout: 10 log: true diff --git a/tests/stack/deploy.test.ts b/tests/stack/deploy.test.ts index f6e2289..7884c69 100644 --- a/tests/stack/deploy.test.ts +++ b/tests/stack/deploy.test.ts @@ -139,7 +139,7 @@ describe('Unit tests for stack deployment', () => { stackName, set( cloneDeep(oneFcOneGatewayIac), - 'functions[0].code', + 'functions[0].code.path', 'tests/fixtures/artifacts/large-artifact.zip', ), { stackName } as ActionContext, From 53c3508cad733620549b9f0e891a8fee07c321c3 Mon Sep 17 00:00:00 2001 From: seven Date: Sat, 22 Feb 2025 00:52:32 +0800 Subject: [PATCH 2/4] feat: container runtime work Signed-off-by: seven --- samples/aliyun-poc-fc-gpu.yml | 8 ++++---- src/stack/rosStack/function.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/samples/aliyun-poc-fc-gpu.yml b/samples/aliyun-poc-fc-gpu.yml index e481f94..a0d63f3 100644 --- a/samples/aliyun-poc-fc-gpu.yml +++ b/samples/aliyun-poc-fc-gpu.yml @@ -22,11 +22,11 @@ tags: functions: insight_poc_fn: - name: insight-poc-gpu-fns + name: insight-poc-gpu-fn container: - image: registry.cn-hangzhou.aliyuncs.com/aliyunfc/runtime/nodejs18:1.8.0 - command: [node, index.handler] - entrypoint: [node] + image: registry.cn-chengdu.aliyuncs.com/geek-fun/meke-api:latest + command: [ npm, start ] + entrypoint: [ node ] port: 9000 memory: 512 timeout: 10 diff --git a/src/stack/rosStack/function.ts b/src/stack/rosStack/function.ts index 1d450d7..202ea76 100644 --- a/src/stack/rosStack/function.ts +++ b/src/stack/rosStack/function.ts @@ -144,7 +144,7 @@ export const resolveFunctions = ( if (fnc.container) { runtimeConfig = { runtime: 'custom-container', - handler: '', + handler: 'index.handler', customContainerConfig: { image: fnc.container.image, command: fnc.container.command, From 2af18608c8bf9cf41e33ab2c61d94727f01481da Mon Sep 17 00:00:00 2001 From: seven Date: Sat, 22 Feb 2025 14:14:23 +0800 Subject: [PATCH 3/4] feat: add test for container fc Signed-off-by: seven --- samples/aliyun-poc-fc-gpu.yml | 3 +-- src/stack/rosStack/function.ts | 3 +-- src/types/domains/function.ts | 6 ++---- src/validator/functionSchema.ts | 5 ++--- tests/fixtures/deployFixture.ts | 36 +++++++++++++++++++++++++++++++++ tests/stack/deploy.test.ts | 18 +++++++++++++++++ 6 files changed, 60 insertions(+), 11 deletions(-) diff --git a/samples/aliyun-poc-fc-gpu.yml b/samples/aliyun-poc-fc-gpu.yml index a0d63f3..ba53ee8 100644 --- a/samples/aliyun-poc-fc-gpu.yml +++ b/samples/aliyun-poc-fc-gpu.yml @@ -25,8 +25,7 @@ functions: name: insight-poc-gpu-fn container: image: registry.cn-chengdu.aliyuncs.com/geek-fun/meke-api:latest - command: [ npm, start ] - entrypoint: [ node ] + cmd: "npm start" port: 9000 memory: 512 timeout: 10 diff --git a/src/stack/rosStack/function.ts b/src/stack/rosStack/function.ts index 202ea76..b668fc0 100644 --- a/src/stack/rosStack/function.ts +++ b/src/stack/rosStack/function.ts @@ -147,8 +147,7 @@ export const resolveFunctions = ( handler: 'index.handler', customContainerConfig: { image: fnc.container.image, - command: fnc.container.command, - entrypoint: fnc.container.entrypoint, + command: fnc.container.cmd?.split(' '), port: fnc.container.port, }, }; diff --git a/src/types/domains/function.ts b/src/types/domains/function.ts index 8c46511..01fcff7 100644 --- a/src/types/domains/function.ts +++ b/src/types/domains/function.ts @@ -7,8 +7,7 @@ export type FunctionRaw = { }; container?: { image: string; - command: Array; - entrypoint: Array; + cmd?: string; port: number; }; memory: number; @@ -45,8 +44,7 @@ export type FunctionDomain = { }; container?: { image: string; - command: Array; - entrypoint: Array; + cmd?: string; port: number; }; memory: number; diff --git a/src/validator/functionSchema.ts b/src/validator/functionSchema.ts index 2fca119..c762ba2 100644 --- a/src/validator/functionSchema.ts +++ b/src/validator/functionSchema.ts @@ -37,12 +37,11 @@ export const functionSchema = { }, container: { type: 'object', - required: ['image', 'command', 'entrypoint', 'port'], + required: ['image', 'port'], additionalProperties: false, properties: { image: { type: 'string' }, - command: { type: 'array', items: { type: 'string' } }, - entrypoint: { type: 'array', items: { type: 'string' } }, + cmd: { type: 'string' }, port: { type: 'number' }, }, }, diff --git a/tests/fixtures/deployFixture.ts b/tests/fixtures/deployFixture.ts index 228f426..4760d82 100644 --- a/tests/fixtures/deployFixture.ts +++ b/tests/fixtures/deployFixture.ts @@ -958,6 +958,42 @@ export const oneFcWithStageRos = { }, }, }; + +export const oneFcWithContainerIac = { + ...oneFcIac, + functions: [ + { + ...((oneFcIac.functions && oneFcIac.functions[0]) ?? {}), + code: undefined, + container: { + image: 'registry.cn-hangzhou.aliyuncs.com/aliyunfc/abcd:1.6.0', + cmd: 'npm start', + port: 9200, + }, + }, + ], +} as ServerlessIac; + +export const oneFcWithContainerRos = { + ...oneFcRos, + Resources: { + ...oneFcRos.Resources, + hello_fn: { + ...oneFcRos.Resources.hello_fn, + Properties: { + ...oneFcRos.Resources.hello_fn.Properties, + Code: undefined, + Runtime: 'custom-container', + CustomContainerConfig: { + Command: ['npm', 'start'], + Image: 'registry.cn-hangzhou.aliyuncs.com/aliyunfc/abcd:1.6.0', + Port: 9200, + }, + }, + }, + }, +}; + export const largeCodeRos = { Description: 'my-demo-service stack', Mappings: { diff --git a/tests/stack/deploy.test.ts b/tests/stack/deploy.test.ts index 7884c69..e201112 100644 --- a/tests/stack/deploy.test.ts +++ b/tests/stack/deploy.test.ts @@ -17,6 +17,8 @@ import { oneFcOneGatewayIac, oneFcOneGatewayRos, oneFcRos, + oneFcWithContainerIac, + oneFcWithContainerRos, oneFcWithStageRos, referredServiceIac, referredServiceRos, @@ -230,4 +232,20 @@ describe('Unit tests for stack deployment', () => { ]); }); }); + + describe('unit test for function with container', () => { + it('should deploy function with container when container field is provided', async () => { + const stackName = 'my-demo-stack-with-container'; + mockedRosStackDeploy.mockResolvedValue(stackName); + + await deployStack(stackName, oneFcWithContainerIac, { stackName } as ActionContext); + + expect(mockedRosStackDeploy).toHaveBeenCalledTimes(2); + expect(mockedRosStackDeploy.mock.calls[1]).toEqual([ + stackName, + oneFcWithContainerRos, + { stackName }, + ]); + }); + }); }); From 17b18bd082d86693ae888e5f17700309ff26ee0f Mon Sep 17 00:00:00 2001 From: seven Date: Sat, 22 Feb 2025 16:31:04 +0800 Subject: [PATCH 4/4] feat: enable support to specify gpu configuration Signed-off-by: seven --- samples/aliyun-poc-fc-gpu.yml | 1 + src/parser/functionParser.ts | 3 ++- src/stack/rosStack/function.ts | 30 ++++++++++++++++++++++++++++-- src/types/domains/function.ts | 13 +++++++++++++ src/validator/functionSchema.ts | 13 +++++++++++++ tests/fixtures/deployFixture.ts | 26 ++++++++++++++++++++++++++ tests/stack/deploy.test.ts | 20 +++++++++++++++++--- 7 files changed, 100 insertions(+), 6 deletions(-) diff --git a/samples/aliyun-poc-fc-gpu.yml b/samples/aliyun-poc-fc-gpu.yml index ba53ee8..1e39889 100644 --- a/samples/aliyun-poc-fc-gpu.yml +++ b/samples/aliyun-poc-fc-gpu.yml @@ -28,6 +28,7 @@ functions: cmd: "npm start" port: 9000 memory: 512 + gpu: TESLA_8 timeout: 10 network: vpc_id: vpc-2vc8v9btc8470laqui9bk diff --git a/src/parser/functionParser.ts b/src/parser/functionParser.ts index c23014a..c0a4496 100644 --- a/src/parser/functionParser.ts +++ b/src/parser/functionParser.ts @@ -1,4 +1,4 @@ -import { FunctionDomain, FunctionRaw, NasStorageClassEnum } from '../types'; +import { FunctionDomain, FunctionGpuEnum, FunctionRaw, NasStorageClassEnum } from '../types'; import { isEmpty } from 'lodash'; export const parseFunction = (functions?: { @@ -13,6 +13,7 @@ export const parseFunction = (functions?: { code: func.code, container: func.container, memory: func.memory, + gpu: func.gpu as FunctionGpuEnum, timeout: func.timeout, environment: func.environment, log: func.log, diff --git a/src/stack/rosStack/function.ts b/src/stack/rosStack/function.ts index b668fc0..ac78371 100644 --- a/src/stack/rosStack/function.ts +++ b/src/stack/rosStack/function.ts @@ -1,4 +1,10 @@ -import { ActionContext, FunctionDomain, NasStorageClassEnum, ServerlessIac } from '../../types'; +import { + ActionContext, + FunctionDomain, + FunctionGpuEnum, + NasStorageClassEnum, + ServerlessIac, +} from '../../types'; import { CODE_ZIP_SIZE_LIMIT, encodeBase64ForRosId, @@ -25,6 +31,7 @@ const storageClassMap = { [NasStorageClassEnum.EXTREME_STANDARD]: { fileSystemType: 'extreme', storageType: 'standard' }, [NasStorageClassEnum.EXTREME_ADVANCE]: { fileSystemType: 'extreme', storageType: 'advance' }, }; + const securityGroupRangeMap: { [key: string]: string } = { TCP: '1/65535', UDP: '1/65535', @@ -32,6 +39,17 @@ const securityGroupRangeMap: { [key: string]: string } = { GRE: '-1/-1', ALL: '-1/-1', }; +const gpuConfigMap = { + [FunctionGpuEnum.TESLA_8]: { gpuMemorySize: 8192, gpuType: 'fc.gpu.tesla.1' }, + [FunctionGpuEnum.TESLA_12]: { gpuMemorySize: 12288, gpuType: 'fc.gpu.tesla.1' }, + [FunctionGpuEnum.TESLA_16]: { gpuMemorySize: 16384, gpuType: 'fc.gpu.tesla.1' }, + [FunctionGpuEnum.AMPERE_8]: { gpuMemorySize: 8192, gpuType: 'fc.gpu.ampere.1' }, + [FunctionGpuEnum.AMPERE_12]: { gpuMemorySize: 12288, gpuType: 'fc.gpu.ampere.1' }, + [FunctionGpuEnum.AMPERE_16]: { gpuMemorySize: 16384, gpuType: 'fc.gpu.ampere.1' }, + [FunctionGpuEnum.AMPERE_24]: { gpuMemorySize: 24576, gpuType: 'fc.gpu.ampere.1' }, + [FunctionGpuEnum.ADA_48]: { gpuMemorySize: 49152, gpuType: 'fc.gpu.ada.1' }, +}; + const transformSecurityRules = (rules: Array, ruleType: 'INGRESS' | 'EGRESS') => { return rules.map((rule) => { const [protocol, cidrIp, portRange] = rule.split(':'); @@ -49,6 +67,13 @@ const transformSecurityRules = (rules: Array, ruleType: 'INGRESS' | 'EGR }); }; +const transformGpuConfig = (gpu: FunctionDomain['gpu']) => { + if (!gpu) { + return undefined; + } + + return gpuConfigMap[gpu]; +}; export const resolveFunctions = ( scope: ros.Construct, functions: Array | undefined, @@ -248,8 +273,9 @@ export const resolveFunctions = ( { functionName: replaceReference(fnc.name, context), memorySize: replaceReference(fnc.memory, context), - timeout: replaceReference(fnc.timeout, context), diskSize: fnc.storage?.disk, + gpuConfig: transformGpuConfig(fnc.gpu), + timeout: replaceReference(fnc.timeout, context), environmentVariables: replaceReference(fnc.environment, context), logConfig, vpcConfig, diff --git a/src/types/domains/function.ts b/src/types/domains/function.ts index 01fcff7..396132a 100644 --- a/src/types/domains/function.ts +++ b/src/types/domains/function.ts @@ -11,6 +11,7 @@ export type FunctionRaw = { port: number; }; memory: number; + gpu: string; timeout: number; log?: boolean; environment?: { @@ -48,6 +49,7 @@ export type FunctionDomain = { port: number; }; memory: number; + gpu?: FunctionGpuEnum; timeout: number; log?: boolean; environment?: { @@ -77,3 +79,14 @@ export enum NasStorageClassEnum { EXTREME_STANDARD = 'EXTREME_STANDARD', EXTREME_ADVANCE = 'EXTREME_ADVANCE', } + +export enum FunctionGpuEnum { + TESLA_8 = 'TESLA_8', + TESLA_12 = 'TESLA_12', + TESLA_16 = 'TESLA_16', + AMPERE_8 = 'AMPERE_8', + AMPERE_12 = 'AMPERE_12', + AMPERE_16 = 'AMPERE_16', + AMPERE_24 = 'AMPERE_24', + ADA_48 = 'ADA_48', +} diff --git a/src/validator/functionSchema.ts b/src/validator/functionSchema.ts index c762ba2..7b53daa 100644 --- a/src/validator/functionSchema.ts +++ b/src/validator/functionSchema.ts @@ -46,6 +46,19 @@ export const functionSchema = { }, }, memory: { type: 'number' }, + gpu: { + type: 'string', + enum: [ + 'TESLA_8', + 'TESLA_12', + 'TESLA_16', + 'AMPERE_8', + 'AMPERE_12', + 'AMPERE_16', + 'AMPERE_24', + 'ADA_48', + ], + }, timeout: { type: 'number' }, log: { type: 'boolean' }, environment: { diff --git a/tests/fixtures/deployFixture.ts b/tests/fixtures/deployFixture.ts index 4760d82..acdb1a2 100644 --- a/tests/fixtures/deployFixture.ts +++ b/tests/fixtures/deployFixture.ts @@ -994,6 +994,32 @@ export const oneFcWithContainerRos = { }, }; +export const oneFcWithGpuIac = { + ...oneFcIac, + functions: [ + { + ...((oneFcIac.functions && oneFcIac.functions[0]) ?? {}), + gpu: 'TESLA_8', + }, + ], +} as ServerlessIac; +export const oneFcWithGpuRos = { + ...oneFcRos, + Resources: { + ...oneFcRos.Resources, + hello_fn: { + ...oneFcRos.Resources.hello_fn, + Properties: { + ...oneFcRos.Resources.hello_fn.Properties, + GpuConfig: { + GpuMemorySize: 8192, + GpuType: 'fc.gpu.tesla.1', + }, + }, + }, + }, +}; + export const largeCodeRos = { Description: 'my-demo-service stack', Mappings: { diff --git a/tests/stack/deploy.test.ts b/tests/stack/deploy.test.ts index e201112..4377885 100644 --- a/tests/stack/deploy.test.ts +++ b/tests/stack/deploy.test.ts @@ -19,6 +19,8 @@ import { oneFcRos, oneFcWithContainerIac, oneFcWithContainerRos, + oneFcWithGpuIac, + oneFcWithGpuRos, oneFcWithStageRos, referredServiceIac, referredServiceRos, @@ -217,7 +219,7 @@ describe('Unit tests for stack deployment', () => { }); }); - describe('unit test for function nas attachment', () => { + describe('unit test for serverless Gpu', () => { it('should deploy function with nas when nas field is provided', async () => { const stackName = 'my-demo-stack-with-nas'; mockedRosStackDeploy.mockResolvedValue(stackName); @@ -231,9 +233,7 @@ describe('Unit tests for stack deployment', () => { { stackName }, ]); }); - }); - describe('unit test for function with container', () => { it('should deploy function with container when container field is provided', async () => { const stackName = 'my-demo-stack-with-container'; mockedRosStackDeploy.mockResolvedValue(stackName); @@ -247,5 +247,19 @@ describe('Unit tests for stack deployment', () => { { stackName }, ]); }); + + it('should deploy function with gpu configured', async () => { + const stackName = 'my-demo-stack-with-gpu'; + mockedRosStackDeploy.mockResolvedValue(stackName); + + await deployStack(stackName, oneFcWithGpuIac, { stackName } as ActionContext); + + expect(mockedRosStackDeploy).toHaveBeenCalledTimes(2); + expect(mockedRosStackDeploy.mock.calls[1]).toEqual([ + stackName, + oneFcWithGpuRos, + { stackName }, + ]); + }); }); });