From 5e25d520127dfc4f61f850f4bb367cf0a95e7712 Mon Sep 17 00:00:00 2001 From: seven Date: Fri, 21 Feb 2025 22:58:37 +0800 Subject: [PATCH 1/3] 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/3] 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/3] 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 }, + ]); + }); + }); });