Skip to content
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
37 changes: 36 additions & 1 deletion __tests__/e2e/nodejs/s_oss_config_auto.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,39 @@ resources:
handler: index.handler
memorySize: 128
timeout: 60
ossMountConfig: auto|mountDir=/mnt/test-oss-bucket|bucketPath=/|rules=[{"allowedOrigin":"*","allowedMethod":["GET","POST","PUT","DELETE","HEAD"],"allowedHeader":"*","exposeHeader":"Content-Length","maxAgeSeconds":30}]
ossMountConfig: auto|mountDir=/mnt/test-oss-bucket|bucketPath=/|rules=[{"allowedOrigin":"*","allowedMethod":["GET","POST","PUT","DELETE","HEAD"],"allowedHeader":"*","exposeHeader":"Content-Length","maxAgeSeconds":30}]

fcDemo3: # 业务名称/模块名称
component: ${env('fc_component_version', path('../../../'))}
actions:
pre-deploy:
- run: npm install
path: ./test-auto-code
props: # 组件的属性值
region: ${vars.region}
functionName: fc3-event-${env('fc_component_function_name', 'nodejs18')}-nasConfig
role: acs:ram::${config('AccountID')}:role/aliyunaliyunfcdefaultrole
runtime: ${env('fc_component_runtime', 'nodejs18')}
code: ./test-auto-code
handler: index.handler
memorySize: 128
timeout: 60
nasConfig: auto|mountDir=/mnt/fcDir|nasDir=/nasDir
vpcConfig: auto

fcDemo4: # 业务名称/模块名称
component: ${env('fc_component_version', path('../../../'))}
actions:
pre-deploy:
- run: npm install
path: ./test-auto-code
props: # 组件的属性值
region: ${vars.region}
functionName: fc3-event-${env('fc_component_function_name', 'nodejs18')}-logConfig
role: acs:ram::${config('AccountID')}:role/aliyunaliyunfcdefaultrole
runtime: ${env('fc_component_runtime', 'nodejs18')}
code: ./test-auto-code
handler: index.handler
memorySize: 128
timeout: 60
logConfig: auto|enableRequestMetrics=false|enableInstanceMetrics=false
2 changes: 1 addition & 1 deletion publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Type: Component
Name: fc3
Provider:
- 阿里云
Version: 0.1.13
Version: 0.1.14
Description: 阿里云函数计算全生命周期管理
HomePage: https://github.com/devsapp/fc3
Organization: 阿里云函数计算(FC)
Expand Down
39 changes: 28 additions & 11 deletions src/subCommands/deploy/impl/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import FC, { GetApiType } from '../../../resources/fc';
import VPC_NAS from '../../../resources/vpc-nas';
import Base from './base';
import { ICredentials } from '@serverless-devs/component-interface';
import { calculateCRC64, getFileSize } from '../../../utils';
import { calculateCRC64, getFileSize, parseAutoConfig, checkFcDir } from '../../../utils';
import OSS from '../../../resources/oss';
import { setNodeModulesBinPermissions } from '../../../resources/fc/impl/utils';

Expand Down Expand Up @@ -349,22 +349,30 @@ export default class Service extends Base {
if (slsAuto) {
const sls = new Sls(region, credential as ICredentials);
const { project, logstore } = await sls.deploy();
const logAutoConfig = parseAutoConfig(this.local.logConfig as string);
const logParams = logAutoConfig?.params || {};
const customFields: Record<string, any> = { ...logParams };
const getConfigValue = (field: string, defaultValue: any) => {
return field in customFields ? customFields[field] : defaultValue;
};
logger.write(
yellow(`Created log resource succeeded, please replace logConfig: auto in yaml with:
logConfig:
enableInstanceMetrics: true
enableRequestMetrics: true
logBeginRule: DefaultRegex
enableInstanceMetrics: ${getConfigValue('enableInstanceMetrics', true)}
enableRequestMetrics: ${getConfigValue('enableRequestMetrics', true)}
logBeginRule: ${getConfigValue('logBeginRule', 'DefaultRegex')}
logstore: ${logstore}
project: ${project}\n`),
);

this.createResource.sls = { project, logstore };
_.set(this.local, 'logConfig', {
enableInstanceMetrics: true,
enableRequestMetrics: true,
logBeginRule: 'DefaultRegex',
enableInstanceMetrics: getConfigValue('enableInstanceMetrics', true),
enableRequestMetrics: getConfigValue('enableRequestMetrics', true),
logBeginRule: getConfigValue('logBeginRule', 'DefaultRegex'),
logstore,
project,
...customFields,
});
}

Expand Down Expand Up @@ -443,7 +451,15 @@ vpcConfig:
_.set(this.local, 'vpcConfig', vpcConfig);
}
if (nasAuto) {
let serverAddr = `${mountTargetDomain}:/${functionName}`;
const { params } = parseAutoConfig(this.local.nasConfig as string);
const fcDir = params.mountDir
? checkFcDir(params.mountDir, 'mountDir')
: `/mnt/${functionName}`;
if (params.nasDir && !params.nasDir.startsWith('/')) {
throw new Error('nasDir must start with /');
}
const nasDir = params.nasDir ? params.nasDir : `/${functionName}`;
let serverAddr = `${mountTargetDomain}:${nasDir}`;
if (serverAddr.length > 128) {
serverAddr = serverAddr.substring(0, 128);
}
Expand All @@ -454,8 +470,9 @@ nasConfig:
userId: 0
mountPoints:
- serverAddr: ${serverAddr}
mountDir: /mnt/${functionName}
enableTLS: false\n`),
mountDir: ${fcDir}
enableTLS: false
`),
);
this.createResource.nas = { mountTargetDomain, fileSystemId };
_.set(this.local, 'nasConfig', {
Expand All @@ -464,7 +481,7 @@ nasConfig:
mountPoints: [
{
serverAddr,
mountDir: `/mnt/${functionName}`,
mountDir: fcDir,
enableTLS: false,
},
],
Expand Down
16 changes: 13 additions & 3 deletions src/subCommands/model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { OSSMountPoint, VPCConfig } from '@alicloud/fc20230330';
import { MODEL_DOWNLOAD_TIMEOUT } from './constants';
import { initClient } from './utils';
import * as $Dev20230714 from '@alicloud/devs20230714';
import { parseAutoConfig, checkFcDir } from '../../utils';

const commandsList = Object.keys(commandsHelp.subCommands);

Expand Down Expand Up @@ -239,7 +240,16 @@ vpcConfig:
}

if (nasAuto) {
let serverAddr = `${mountTargetDomain}:/${functionName}`;
const nasAutoConfig = parseAutoConfig(this.local.nasConfig as string);
const params = nasAutoConfig?.params || {};
const fcDir = params.mountDir
? checkFcDir(params.mountDir, 'mountDir')
: `/mnt/${functionName}`;
if (params.nasDir && !params.nasDir.startsWith('/')) {
throw new Error('nasDir must start with /');
}
const nasDir = params.nasDir ? params.nasDir : `/${functionName}`;
let serverAddr = `${mountTargetDomain}:${nasDir}`;
if (serverAddr.length > 128) {
serverAddr = serverAddr.substring(0, 128);
}
Expand All @@ -250,7 +260,7 @@ groupId: 0
userId: 0
mountPoints:
- serverAddr: ${serverAddr}
mountDir: /mnt/${functionName}
mountDir: ${fcDir}
enableTLS: false\n`),
);
this.createResource.nas = { mountTargetDomain, fileSystemId };
Expand All @@ -260,7 +270,7 @@ mountPoints:
mountPoints: [
{
serverAddr,
mountDir: `/mnt/${functionName}`,
mountDir: fcDir,
enableTLS: false,
},
],
Expand Down
85 changes: 85 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,40 @@ export const isAuto = (config: unknown): boolean => {
return _.toUpper(autoConfig) === 'AUTO';
};

export const parseAutoConfig = (
config: string,
): { isAuto: boolean; params: Record<string, any> } => {
if (!_.isString(config)) {
return { isAuto: false, params: {} };
}

const parts = config.split('|');
const baseConfig = parts[0];
logger.debug(`parseAutoConfig, baseConfig = ${baseConfig}`);

if (_.toUpper(baseConfig) === 'AUTO') {
const params: Record<string, any> = {};
for (let i = 1; i < parts.length; i++) {
const [key, value] = parts[i].split('=');
if (key && value) {
const trimmedValue = value.trim();
if (typeof trimmedValue === 'string') {
try {
params[key.trim()] = JSON.parse(trimmedValue);
} catch (error) {
params[key.trim()] = trimmedValue;
}
} else {
params[key.trim()] = trimmedValue;
}
}
}
return { isAuto: true, params };
}

return { isAuto: false, params: {} };
};

export const isAutoVpcConfig = (config: unknown): boolean => {
logger.debug(`isAutoVpcConfig, vpcConfig = ${JSON.stringify(config)}`);
if (_.isString(config)) {
Expand Down Expand Up @@ -223,3 +257,54 @@ export function getUserAgent(userAgent: string, command: string) {
}
return `${function_ai}Component:fc3;Nodejs:${process.version};OS:${process.platform}-${process.arch}command:${command}`;
}

/**
* 验证并规范化路径
*/
export function checkFcDir(path: string, paramName = 'path'): string {
const normalizedPath = path.trim();

if (!normalizedPath.startsWith('/')) {
throw new Error(`${paramName} does not start with '/'`);
}

// 检查路径长度
if (normalizedPath.length > 128) {
throw new Error(
`${paramName} is too long (${normalizedPath.length}), maximum length is 128 characters`,
);
}

// 检查不允许的系统目录前缀
const forbiddenPrefixes = [
'/bin',
'/boot',
'/dev',
'/etc',
'/lib',
'/lib64',
'/media',
'/opt',
'/proc',
'/root',
'/run',
'/sbin',
'/srv',
'/sys',
'/usr',
'/var',
];

for (const prefix of forbiddenPrefixes) {
if (normalizedPath.startsWith(prefix)) {
throw new Error(`Invalid ${paramName}, ${prefix} and its subdirectories are not allowed`);
}
}

const isValidTwoLevelPath = /^\/[^/]+\/.+/.test(normalizedPath);
if (!isValidTwoLevelPath) {
throw new Error(`Invalid ${paramName}, path must be in /**/** format with at least two levels`);
}

return normalizedPath;
}
Loading