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
110 changes: 94 additions & 16 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@
"@alicloud/openapi-client": "^0.4.12",
"@alicloud/ros-cdk-apigateway": "^1.6.0",
"@alicloud/ros-cdk-core": "^1.6.0",
"@alicloud/ros-cdk-ecs": "^1.6.0",
"@alicloud/ros-cdk-elasticsearchserverless": "^1.6.0",
"@alicloud/ros-cdk-fc3": "^1.6.0",
"@alicloud/ros-cdk-nas": "^1.6.0",
"@alicloud/ros-cdk-oss": "^1.6.0",
"@alicloud/ros-cdk-ossdeployment": "^1.6.0",
"@alicloud/ros-cdk-ram": "^1.6.0",
Expand Down
58 changes: 58 additions & 0 deletions samples/aliyun-poc-fc-gpu.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
version: 0.0.1
provider:
name: aliyun
region: cn-chengdu

vars:
testv: testVarValue
handler: index.handler

stages:
default:
node_env: default
dev:
node_env: development
prod:
region: cn-shanghai

service: insight-poc-gpu

tags:
owner: geek-fun

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]
entrypoint: [node]
port: 9000
memory: 512
timeout: 10
network:
vpc_id: vpc-2vc8v9btc8470laqui9bk
subnet_ids:
- vsw-2vc9zrs5mojkxd14yo3zw
- vsw-2vceshdo0xjp9q9t0oyt0
security_group:
name: insight-poc-gpu-fn-sg
ingress:
- TCP:0.0.0.0/0:80
- TCP:0.0.0.0/0:443
- TCP:0.0.0.0/0:22/22
- ICMP:0.0.0.0/0:ALL
egress:
- ALL:0.0.0.0/0:ALL
storage:
nas:
- mount_path: /mnt/nas
storage_class: STANDARD_CAPACITY
environment:
NODE_ENV: ${stages.node_env}
TEST_VAR: ${vars.testv}
TEST_VAR_EXTRA: abcds-${vars.testv}-andyou
10 changes: 9 additions & 1 deletion src/parser/functionParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FunctionDomain, FunctionRaw } from '../types';
import { FunctionDomain, FunctionRaw, NasStorageClassEnum } from '../types';
import { isEmpty } from 'lodash';

export const parseFunction = (functions?: {
Expand All @@ -17,5 +17,13 @@ export const parseFunction = (functions?: {
environment: func.environment,
code: func.code,
log: func.log,
network: func.network,
storage: {
disk: func.storage?.disk,
nas: func.storage?.nas?.map((nasItem) => ({
mount_path: nasItem.mount_path,
storage_class: nasItem.storage_class as NasStorageClassEnum,
})),
},
}));
};
126 changes: 125 additions & 1 deletion src/stack/rosStack/function.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ActionContext, FunctionDomain, ServerlessIac } from '../../types';
import { ActionContext, FunctionDomain, NasStorageClassEnum, ServerlessIac } from '../../types';
import {
CODE_ZIP_SIZE_LIMIT,
encodeBase64ForRosId,
getFileSource,
readCodeSize,
replaceReference,
Expand All @@ -11,8 +12,43 @@ import { isEmpty } from 'lodash';
import * as ossDeployment from '@alicloud/ros-cdk-ossdeployment';
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';

const storageClassMap = {
[NasStorageClassEnum.STANDARD_CAPACITY]: { fileSystemType: 'standard', storageType: 'Capacity' },
[NasStorageClassEnum.STANDARD_PERFORMANCE]: {
fileSystemType: 'standard',
storageType: 'Performance',
},
[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',
ICMP: '-1/-1',
GRE: '-1/-1',
ALL: '-1/-1',
};
const transformSecurityRules = (rules: Array<string>, ruleType: 'INGRESS' | 'EGRESS') => {
return rules.map((rule) => {
const [protocol, cidrIp, portRange] = rule.split(':');

return {
ipProtocol: protocol.toLowerCase(),
portRange:
portRange.toUpperCase() === 'ALL'
? securityGroupRangeMap[protocol.toUpperCase()]
: portRange.includes('/')
? portRange
: `${portRange}/${portRange}`,
[ruleType === 'INGRESS' ? 'sourceCidrIp' : 'destCidrIp']: cidrIp,
};
});
};

export const resolveFunctions = (
scope: ros.Construct,
functions: Array<FunctionDomain> | undefined,
Expand Down Expand Up @@ -102,6 +138,79 @@ export const resolveFunctions = (
)?.objectKey,
};
}

let vpcConfig: fc.RosFunction.VpcConfigProperty | undefined = undefined;
if (fnc.network) {
const securityGroup = new ecs.SecurityGroup(
scope,
`${fnc.key}_security_group`,
{
securityGroupName: fnc.network.security_group.name,
vpcId: replaceReference(fnc.network.vpc_id, context),
tags: replaceReference(tags, context),
securityGroupIngress: transformSecurityRules(
fnc.network.security_group.ingress,
'INGRESS',
),
securityGroupEgress: transformSecurityRules(fnc.network.security_group.egress, 'EGRESS'),
},
true,
);

vpcConfig = {
vpcId: replaceReference(fnc.network.vpc_id, context),
vSwitchIds: replaceReference(fnc.network.subnet_ids, context),
securityGroupId: securityGroup.attrSecurityGroupId,
};
}

let fcNas:
| Array<{ nas: nas.FileSystem; nasMount: nas.MountTarget; mountDir: string }>
| undefined;
if (fnc.storage?.nas) {
fcNas = fnc.storage.nas.map((nasItem) => {
const { fileSystemType, storageType } = storageClassMap[nasItem.storage_class];
const accessGroup = new nas.AccessGroup(
scope,
`${fnc.key}_nas_access_${encodeBase64ForRosId(nasItem.mount_path)}`,
{
accessGroupName: `${fnc.name}-nas-access-${encodeBase64ForRosId(nasItem.mount_path)}`,
accessGroupType: 'Vpc',
},
true,
);

const nasResource = new nas.FileSystem(
scope,
`${fnc.key}_nas_${encodeBase64ForRosId(nasItem.mount_path)}`,
{
fileSystemType,
storageType,
protocolType: 'NFS',
tags: [
...(replaceReference(tags, context) ?? []),
{ key: 'function-name', value: fnc.name },
],
},
true,
);
const nasMountTarget = new nas.MountTarget(
scope,
`${fnc.key}_nas_mount_${encodeBase64ForRosId(nasItem.mount_path)}`,
{
fileSystemId: nasResource.attrFileSystemId,
networkType: 'Vpc',
accessGroupName: accessGroup.attrAccessGroupName,
vpcId: fnc.network!.vpc_id,
vSwitchId: fnc.network!.subnet_ids[0],
},
true,
);

return { nas: nasResource, nasMount: nasMountTarget, mountDir: nasItem.mount_path };
});
}

const fcn = new fc.RosFunction(
scope,
fnc.key,
Expand All @@ -111,9 +220,19 @@ export const resolveFunctions = (
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,
nasConfig: fcNas?.length
? {
mountPoints: fcNas?.map(({ nasMount, mountDir }) => ({
mountDir,
serverAddr: `${nasMount.attrMountTargetDomain}:/`,
})),
}
: undefined,
},
true,
);
Expand All @@ -126,5 +245,10 @@ export const resolveFunctions = (
if (storeInBucket) {
fcn.addRosDependency(`${service}_artifacts_code_deployment`);
}
if (fcNas?.length) {
fcNas.forEach((nasItem) => {
fcn.addRosDependency(`${fnc.key}_nas_mount_${encodeBase64ForRosId(nasItem.mountDir)}`);
});
}
});
};
51 changes: 50 additions & 1 deletion src/types/domains/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,57 @@ export type FunctionRaw = {
environment?: {
[key: string]: string;
};
network?: {
vpc_id: string;
subnet_ids: Array<string>;
security_group: {
name: string;
ingress: Array<string>;
egress: Array<string>;
};
};
storage?: {
disk?: number;
nas?: Array<{
mount_path: string;
storage_class: string;
}>;
};
};

export type FunctionDomain = FunctionRaw & {
export type FunctionDomain = {
key: string;
name: string;
runtime: string;
handler: string;
code: string;
memory: number;
timeout: number;
log?: boolean;
environment?: {
[key: string]: string;
};
network?: {
vpc_id: string;
subnet_ids: Array<string>;
security_group: {
name: string;
ingress: Array<string>;
egress: Array<string>;
};
};
storage: {
disk?: number;
nas?: Array<{
mount_path: string;
storage_class: NasStorageClassEnum;
}>;
};
};

export enum NasStorageClassEnum {
STANDARD_PERFORMANCE = 'STANDARD_PERFORMANCE',
STANDARD_CAPACITY = 'STANDARD_CAPACITY',
EXTREME_STANDARD = 'EXTREME_STANDARD',
EXTREME_ADVANCE = 'EXTREME_ADVANCE',
}
44 changes: 44 additions & 0 deletions src/validator/functionSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,50 @@ export const functionSchema = {
type: ['string', 'number', 'boolean'],
},
},
network: {
type: 'object',
properties: {
vpc_id: { type: 'string' },
subnet_ids: { type: 'array', items: { type: 'string' } },
security_group: {
type: 'object',
properties: {
name: { type: 'string' },
ingress: { type: 'array', items: { type: 'string' } },
egress: { type: 'array', items: { type: 'string' } },
},
required: ['name', 'ingress'],
additionalProperties: false,
},
},
required: ['vpc_id', 'subnet_ids', 'security_group'],
},
storage: {
type: 'object',
properties: {
disk: { type: 'number' },
nas: {
type: 'array',
items: {
type: 'object',
properties: {
mount_path: { type: 'string' },
storage_class: {
type: 'string',
enum: [
'STANDARD_PERFORMANCE',
'STANDARD_CAPACITY',
'EXTREME_STANDARD',
'EXTREME_ADVANCE',
],
},
additionalProperties: false,
},
required: ['mount_path', 'storage_class'],
},
},
},
},
},
additionalProperties: false,
},
Expand Down
Loading