From 8ed590f4a34dd2896d41accb2154100fbf560f7e Mon Sep 17 00:00:00 2001 From: liuzewen99 Date: Thu, 2 Jul 2026 14:32:53 +0800 Subject: [PATCH] feat: Add support for micro-sandbox configuration --- __tests__/complete-props.yaml | 4 + __tests__/ut/local/local_test.ts | 38 ++++++ __tests__/ut/resources/fc/impl/client_test.ts | 112 +++++++++++++++++- package-lock.json | 15 +-- package.json | 2 +- src/interface/function.ts | 7 ++ src/schema.json | 17 +++ 7 files changed, 186 insertions(+), 9 deletions(-) diff --git a/__tests__/complete-props.yaml b/__tests__/complete-props.yaml index 05fd7f13..9154f9bb 100644 --- a/__tests__/complete-props.yaml +++ b/__tests__/complete-props.yaml @@ -77,6 +77,10 @@ resources: project: string memorySize: 512 instanceConcurrency: 1 # 该参数仅针对 custom/custom.debian10/custom.debian11/custom-container runtime 有效,范围为 [1, 200] + microSandboxConfig: + osType: string + readyCommand: string + startCommand: string nasConfig: groupId: 65534 mountPoints: diff --git a/__tests__/ut/local/local_test.ts b/__tests__/ut/local/local_test.ts index cac5b85e..4f18f322 100644 --- a/__tests__/ut/local/local_test.ts +++ b/__tests__/ut/local/local_test.ts @@ -339,6 +339,20 @@ describe('ComponentLocal', () => { expect(mockInstance.invoke).toHaveBeenCalled(); }); + it('should route micro-sandbox runtime to CustomContainerLocalInvoke', async () => { + mockInputs.props.runtime = 'micro-sandbox'; + const { + CustomContainerLocalInvoke, + } = require('../../../src/subCommands/local/impl/invoke/customContainerLocalInvoke'); + const mockInstance = { invoke: jest.fn().mockResolvedValue(undefined) }; + (CustomContainerLocalInvoke as jest.Mock).mockImplementation(() => mockInstance); + + await componentLocal.invoke(mockInputs); + + expect(CustomContainerLocalInvoke).toHaveBeenCalledWith(mockInputs); + expect(mockInstance.invoke).toHaveBeenCalled(); + }); + it('should warn when function has http trigger', async () => { mockInputs.props.runtime = 'nodejs18'; mockInputs.props.triggers = [ @@ -557,6 +571,30 @@ describe('ComponentLocal', () => { expect(mockInstance.start).toHaveBeenCalled(); }); + it('should route micro-sandbox runtime to CustomContainerLocalStart', async () => { + mockInputs.props.runtime = 'micro-sandbox'; + mockInputs.props.triggers = [ + { + triggerType: 'http', + triggerName: 'httpTrigger', + triggerConfig: { + authType: 'anonymous', + methods: ['GET'], + }, + }, + ]; + const { + CustomContainerLocalStart, + } = require('../../../src/subCommands/local/impl/start/customContainerLocalStart'); + const mockInstance = { start: jest.fn().mockResolvedValue(undefined) }; + (CustomContainerLocalStart as jest.Mock).mockImplementation(() => mockInstance); + + await componentLocal.start(mockInputs); + + expect(CustomContainerLocalStart).toHaveBeenCalledWith(mockInputs); + expect(mockInstance.start).toHaveBeenCalled(); + }); + it('should log error when function does not have http trigger', async () => { mockInputs.props.runtime = 'nodejs18'; mockInputs.props.triggers = [ diff --git a/__tests__/ut/resources/fc/impl/client_test.ts b/__tests__/ut/resources/fc/impl/client_test.ts index af250738..03ae7e08 100644 --- a/__tests__/ut/resources/fc/impl/client_test.ts +++ b/__tests__/ut/resources/fc/impl/client_test.ts @@ -2,13 +2,20 @@ import FC_Client, { fc2Client } from '../../../../../src/resources/fc/impl/clien import { ICredentials } from '@serverless-devs/component-interface'; import { Config } from '@alicloud/openapi-client'; import FC2 from '@alicloud/fc2'; -import { IRegion } from '../../../../../src/interface'; +import { IRegion, IFunction } from '../../../../../src/interface'; import * as utils from '../../../../../src/resources/fc/impl/utils'; import _ from 'lodash'; // Mock external dependencies jest.mock('@alicloud/openapi-client'); jest.mock('@alicloud/fc2'); +jest.mock('@alicloud/fc20230330', () => { + const actual = jest.requireActual('@alicloud/fc20230330'); + return Object.assign({}, actual, { + __esModule: true, + default: jest.fn().mockImplementation(() => ({})), + }); +}); jest.mock('../../../../../src/resources/fc/impl/utils', () => ({ ...jest.requireActual('../../../../../src/resources/fc/impl/utils'), getCustomEndpoint: jest.fn(), @@ -201,4 +208,107 @@ describe('FC_Client', () => { expect(result).toBe(false); }); }); + + describe('createFunction', () => { + let client: FC_Client; + + beforeEach(() => { + (utils.getCustomEndpoint as jest.Mock).mockReturnValue({ + protocol: 'https', + host: 'test-endpoint.com', + endpoint: 'https://test-endpoint.com', + }); + client = new FC_Client(mockRegion, mockCredentials, mockOptions); + }); + + it('should forward microSandboxConfig to the request body', async () => { + const createFunctionWithOptions = jest.fn().mockResolvedValue({} as any); + Object.defineProperty(client, 'fc20230330Client', { + value: { createFunctionWithOptions }, + writable: true, + }); + + const config: IFunction = { + functionName: 'test-function', + runtime: 'micro-sandbox', + microSandboxConfig: { + osType: 'linux', + readyCommand: 'echo ready', + startCommand: 'echo start', + }, + } as IFunction; + + await client.createFunction(config); + + expect(createFunctionWithOptions).toHaveBeenCalledTimes(1); + const request = createFunctionWithOptions.mock.calls[0][0]; + const bodyMap = request.body.toMap(); + expect(bodyMap.runtime).toBe('micro-sandbox'); + expect(bodyMap.microSandboxConfig).toEqual({ + osType: 'linux', + readyCommand: 'echo ready', + startCommand: 'echo start', + }); + }); + + it('should not set microSandboxConfig when it is not provided', async () => { + const createFunctionWithOptions = jest.fn().mockResolvedValue({} as any); + Object.defineProperty(client, 'fc20230330Client', { + value: { createFunctionWithOptions }, + writable: true, + }); + + await client.createFunction({ + functionName: 'test-function', + runtime: 'nodejs18', + } as IFunction); + + const bodyMap = createFunctionWithOptions.mock.calls[0][0].body.toMap(); + expect(bodyMap.microSandboxConfig).toBeUndefined(); + }); + }); + + describe('updateFunction', () => { + let client: FC_Client; + + beforeEach(() => { + (utils.getCustomEndpoint as jest.Mock).mockReturnValue({ + protocol: 'https', + host: 'test-endpoint.com', + endpoint: 'https://test-endpoint.com', + }); + client = new FC_Client(mockRegion, mockCredentials, mockOptions); + }); + + it('should forward microSandboxConfig to the update request body', async () => { + const updateFunctionWithOptions = jest.fn().mockResolvedValue({} as any); + Object.defineProperty(client, 'fc20230330Client', { + value: { updateFunctionWithOptions }, + writable: true, + }); + + const config: IFunction = { + functionName: 'test-function', + runtime: 'micro-sandbox', + microSandboxConfig: { + osType: 'linux', + readyCommand: 'echo ready', + startCommand: 'echo start', + }, + } as IFunction; + + await client.updateFunction(config); + + expect(updateFunctionWithOptions).toHaveBeenCalledTimes(1); + // first positional arg is functionName, second is the request + expect(updateFunctionWithOptions.mock.calls[0][0]).toBe('test-function'); + const request = updateFunctionWithOptions.mock.calls[0][1]; + const bodyMap = request.body.toMap(); + expect(bodyMap.microSandboxConfig).toEqual({ + osType: 'linux', + readyCommand: 'echo ready', + startCommand: 'echo start', + }); + }); + }); }); diff --git a/package-lock.json b/package-lock.json index ea39bb79..82047360 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "dependencies": { "@alicloud/devs20230714": "^2.5.0", "@alicloud/fc2": "^2.6.6", - "@alicloud/fc20230330": "4.7.5", + "@alicloud/fc20230330": "4.7.7", "@alicloud/pop-core": "^1.8.0", "@serverless-cd/srm-aliyun-oss": "^0.0.1-beta.8", "@serverless-cd/srm-aliyun-pop-core": "^0.0.8-beta.1", @@ -215,9 +215,10 @@ } }, "node_modules/@alicloud/fc20230330": { - "version": "4.7.5", - "resolved": "https://registry.npmmirror.com/@alicloud/fc20230330/-/fc20230330-4.7.5.tgz", - "integrity": "sha512-LKaRo2gj4HDCVOckNtDhGQQuTWyiPm+xA/BZ5BmFn4FTyV9O7jhRRSztcHdht9NfOuSQudTTyUhLl082kUrhsA==", + "version": "4.7.7", + "resolved": "https://packages.aliyun.com/670e108663cd360abfe4be65/npm/npm-registry/@alicloud/fc20230330/-/fc20230330-4.7.7.tgz", + "integrity": "sha512-bQG3gYxVCr+b0eah9xpOfiTewTyTeEhdI6px/oki7MDILm/CAQdjuJ2i4sNJslHNz/UQHuf/ILQUSbDwfxb4dw==", + "license": "Apache-2.0", "dependencies": { "@alicloud/openapi-core": "^1.0.0", "@darabonba/typescript": "^1.0.0" @@ -15783,9 +15784,9 @@ } }, "@alicloud/fc20230330": { - "version": "4.7.5", - "resolved": "https://registry.npmmirror.com/@alicloud/fc20230330/-/fc20230330-4.7.5.tgz", - "integrity": "sha512-LKaRo2gj4HDCVOckNtDhGQQuTWyiPm+xA/BZ5BmFn4FTyV9O7jhRRSztcHdht9NfOuSQudTTyUhLl082kUrhsA==", + "version": "4.7.7", + "resolved": "https://packages.aliyun.com/670e108663cd360abfe4be65/npm/npm-registry/@alicloud/fc20230330/-/fc20230330-4.7.7.tgz", + "integrity": "sha512-bQG3gYxVCr+b0eah9xpOfiTewTyTeEhdI6px/oki7MDILm/CAQdjuJ2i4sNJslHNz/UQHuf/ILQUSbDwfxb4dw==", "requires": { "@alicloud/openapi-core": "^1.0.0", "@darabonba/typescript": "^1.0.0" diff --git a/package.json b/package.json index 688d7d87..c25c2d0a 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "dependencies": { "@alicloud/devs20230714": "^2.5.0", "@alicloud/fc2": "^2.6.6", - "@alicloud/fc20230330": "4.7.5", + "@alicloud/fc20230330": "4.7.7", "@alicloud/pop-core": "^1.8.0", "@serverless-cd/srm-aliyun-oss": "^0.0.1-beta.8", "@serverless-cd/srm-aliyun-pop-core": "^0.0.8-beta.1", diff --git a/src/interface/function.ts b/src/interface/function.ts index 37aa46b1..d3fe6082 100644 --- a/src/interface/function.ts +++ b/src/interface/function.ts @@ -75,6 +75,12 @@ export interface ILogConfig { logBeginRule?: 'DefaultRegex' | 'None'; } +export interface IMicroSandboxConfig { + osType?: string; + readyCommand?: string; + startCommand?: string; +} + export interface INasConfig { userId: number; groupId: number; @@ -141,6 +147,7 @@ export interface IFunction { resourceGroupId?: string; logConfig?: 'auto' | ILogConfig; + microSandboxConfig?: IMicroSandboxConfig; nasConfig?: 'auto' | INasConfig; ossMountConfig?: 'auto' | IOssMountConfig; role?: 'auto' | string; diff --git a/src/schema.json b/src/schema.json index b4fb6250..c887c399 100644 --- a/src/schema.json +++ b/src/schema.json @@ -637,6 +637,20 @@ ], "type": "object" }, + "IMicroSandboxConfig": { + "properties": { + "osType": { + "type": "string" + }, + "readyCommand": { + "type": "string" + }, + "startCommand": { + "type": "string" + } + }, + "type": "object" + }, "INasConfig": { "properties": { "groupId": { @@ -1313,6 +1327,9 @@ "minimum": 128, "maximum": 32768 }, + "microSandboxConfig": { + "$ref": "#/definitions/IMicroSandboxConfig" + }, "nasConfig": { "anyOf": [ {