Skip to content

Commit c4ae761

Browse files
authored
feat: enable support for functions to mount nas (#47)
feat: enable support for functions to mount nas - add fc nas configuration - add fc vpc configuration - tests Refs: #40 --------- Signed-off-by: seven <[email protected]>
1 parent 46117da commit c4ae761

File tree

9 files changed

+511
-3
lines changed

9 files changed

+511
-3
lines changed

package-lock.json

+46
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,10 @@
5353
"@alicloud/openapi-client": "^0.4.12",
5454
"@alicloud/ros-cdk-apigateway": "^1.6.0",
5555
"@alicloud/ros-cdk-core": "^1.6.0",
56+
"@alicloud/ros-cdk-ecs": "^1.6.0",
5657
"@alicloud/ros-cdk-elasticsearchserverless": "^1.6.0",
5758
"@alicloud/ros-cdk-fc3": "^1.6.0",
59+
"@alicloud/ros-cdk-nas": "^1.6.0",
5860
"@alicloud/ros-cdk-oss": "^1.6.0",
5961
"@alicloud/ros-cdk-ossdeployment": "^1.6.0",
6062
"@alicloud/ros-cdk-ram": "^1.6.0",

samples/aliyun-poc-fc-gpu.yml

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
version: 0.0.1
2+
provider:
3+
name: aliyun
4+
region: cn-chengdu
5+
6+
vars:
7+
testv: testVarValue
8+
handler: index.handler
9+
10+
stages:
11+
default:
12+
node_env: default
13+
dev:
14+
node_env: development
15+
prod:
16+
region: cn-shanghai
17+
18+
service: insight-poc-gpu
19+
20+
tags:
21+
owner: geek-fun
22+
23+
functions:
24+
insight_poc_fn:
25+
name: insight-poc-gpu-fns
26+
code:
27+
runtime: nodejs18
28+
handler: ${vars.handler}
29+
path: tests/fixtures/artifacts/artifact.zip
30+
container:
31+
image: registry.cn-hangzhou.aliyuncs.com/aliyunfc/runtime/nodejs18:1.8.0
32+
command: [node, index.handler]
33+
entrypoint: [node]
34+
port: 9000
35+
memory: 512
36+
timeout: 10
37+
network:
38+
vpc_id: vpc-2vc8v9btc8470laqui9bk
39+
subnet_ids:
40+
- vsw-2vc9zrs5mojkxd14yo3zw
41+
- vsw-2vceshdo0xjp9q9t0oyt0
42+
security_group:
43+
name: insight-poc-gpu-fn-sg
44+
ingress:
45+
- TCP:0.0.0.0/0:80
46+
- TCP:0.0.0.0/0:443
47+
- TCP:0.0.0.0/0:22/22
48+
- ICMP:0.0.0.0/0:ALL
49+
egress:
50+
- ALL:0.0.0.0/0:ALL
51+
storage:
52+
nas:
53+
- mount_path: /mnt/nas
54+
storage_class: STANDARD_CAPACITY
55+
environment:
56+
NODE_ENV: ${stages.node_env}
57+
TEST_VAR: ${vars.testv}
58+
TEST_VAR_EXTRA: abcds-${vars.testv}-andyou

src/parser/functionParser.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FunctionDomain, FunctionRaw } from '../types';
1+
import { FunctionDomain, FunctionRaw, NasStorageClassEnum } from '../types';
22
import { isEmpty } from 'lodash';
33

44
export const parseFunction = (functions?: {
@@ -17,5 +17,13 @@ export const parseFunction = (functions?: {
1717
environment: func.environment,
1818
code: func.code,
1919
log: func.log,
20+
network: func.network,
21+
storage: {
22+
disk: func.storage?.disk,
23+
nas: func.storage?.nas?.map((nasItem) => ({
24+
mount_path: nasItem.mount_path,
25+
storage_class: nasItem.storage_class as NasStorageClassEnum,
26+
})),
27+
},
2028
}));
2129
};

src/stack/rosStack/function.ts

+125-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { ActionContext, FunctionDomain, ServerlessIac } from '../../types';
1+
import { ActionContext, FunctionDomain, NasStorageClassEnum, ServerlessIac } from '../../types';
22
import {
33
CODE_ZIP_SIZE_LIMIT,
4+
encodeBase64ForRosId,
45
getFileSource,
56
readCodeSize,
67
replaceReference,
@@ -11,8 +12,43 @@ import { isEmpty } from 'lodash';
1112
import * as ossDeployment from '@alicloud/ros-cdk-ossdeployment';
1213
import * as ros from '@alicloud/ros-cdk-core';
1314
import * as sls from '@alicloud/ros-cdk-sls';
15+
import * as nas from '@alicloud/ros-cdk-nas';
16+
import * as ecs from '@alicloud/ros-cdk-ecs';
1417
import { RosFunction } from '@alicloud/ros-cdk-fc3/lib/fc3.generated';
1518

19+
const storageClassMap = {
20+
[NasStorageClassEnum.STANDARD_CAPACITY]: { fileSystemType: 'standard', storageType: 'Capacity' },
21+
[NasStorageClassEnum.STANDARD_PERFORMANCE]: {
22+
fileSystemType: 'standard',
23+
storageType: 'Performance',
24+
},
25+
[NasStorageClassEnum.EXTREME_STANDARD]: { fileSystemType: 'extreme', storageType: 'standard' },
26+
[NasStorageClassEnum.EXTREME_ADVANCE]: { fileSystemType: 'extreme', storageType: 'advance' },
27+
};
28+
const securityGroupRangeMap: { [key: string]: string } = {
29+
TCP: '1/65535',
30+
UDP: '1/65535',
31+
ICMP: '-1/-1',
32+
GRE: '-1/-1',
33+
ALL: '-1/-1',
34+
};
35+
const transformSecurityRules = (rules: Array<string>, ruleType: 'INGRESS' | 'EGRESS') => {
36+
return rules.map((rule) => {
37+
const [protocol, cidrIp, portRange] = rule.split(':');
38+
39+
return {
40+
ipProtocol: protocol.toLowerCase(),
41+
portRange:
42+
portRange.toUpperCase() === 'ALL'
43+
? securityGroupRangeMap[protocol.toUpperCase()]
44+
: portRange.includes('/')
45+
? portRange
46+
: `${portRange}/${portRange}`,
47+
[ruleType === 'INGRESS' ? 'sourceCidrIp' : 'destCidrIp']: cidrIp,
48+
};
49+
});
50+
};
51+
1652
export const resolveFunctions = (
1753
scope: ros.Construct,
1854
functions: Array<FunctionDomain> | undefined,
@@ -102,6 +138,79 @@ export const resolveFunctions = (
102138
)?.objectKey,
103139
};
104140
}
141+
142+
let vpcConfig: fc.RosFunction.VpcConfigProperty | undefined = undefined;
143+
if (fnc.network) {
144+
const securityGroup = new ecs.SecurityGroup(
145+
scope,
146+
`${fnc.key}_security_group`,
147+
{
148+
securityGroupName: fnc.network.security_group.name,
149+
vpcId: replaceReference(fnc.network.vpc_id, context),
150+
tags: replaceReference(tags, context),
151+
securityGroupIngress: transformSecurityRules(
152+
fnc.network.security_group.ingress,
153+
'INGRESS',
154+
),
155+
securityGroupEgress: transformSecurityRules(fnc.network.security_group.egress, 'EGRESS'),
156+
},
157+
true,
158+
);
159+
160+
vpcConfig = {
161+
vpcId: replaceReference(fnc.network.vpc_id, context),
162+
vSwitchIds: replaceReference(fnc.network.subnet_ids, context),
163+
securityGroupId: securityGroup.attrSecurityGroupId,
164+
};
165+
}
166+
167+
let fcNas:
168+
| Array<{ nas: nas.FileSystem; nasMount: nas.MountTarget; mountDir: string }>
169+
| undefined;
170+
if (fnc.storage?.nas) {
171+
fcNas = fnc.storage.nas.map((nasItem) => {
172+
const { fileSystemType, storageType } = storageClassMap[nasItem.storage_class];
173+
const accessGroup = new nas.AccessGroup(
174+
scope,
175+
`${fnc.key}_nas_access_${encodeBase64ForRosId(nasItem.mount_path)}`,
176+
{
177+
accessGroupName: `${fnc.name}-nas-access-${encodeBase64ForRosId(nasItem.mount_path)}`,
178+
accessGroupType: 'Vpc',
179+
},
180+
true,
181+
);
182+
183+
const nasResource = new nas.FileSystem(
184+
scope,
185+
`${fnc.key}_nas_${encodeBase64ForRosId(nasItem.mount_path)}`,
186+
{
187+
fileSystemType,
188+
storageType,
189+
protocolType: 'NFS',
190+
tags: [
191+
...(replaceReference(tags, context) ?? []),
192+
{ key: 'function-name', value: fnc.name },
193+
],
194+
},
195+
true,
196+
);
197+
const nasMountTarget = new nas.MountTarget(
198+
scope,
199+
`${fnc.key}_nas_mount_${encodeBase64ForRosId(nasItem.mount_path)}`,
200+
{
201+
fileSystemId: nasResource.attrFileSystemId,
202+
networkType: 'Vpc',
203+
accessGroupName: accessGroup.attrAccessGroupName,
204+
vpcId: fnc.network!.vpc_id,
205+
vSwitchId: fnc.network!.subnet_ids[0],
206+
},
207+
true,
208+
);
209+
210+
return { nas: nasResource, nasMount: nasMountTarget, mountDir: nasItem.mount_path };
211+
});
212+
}
213+
105214
const fcn = new fc.RosFunction(
106215
scope,
107216
fnc.key,
@@ -111,9 +220,19 @@ export const resolveFunctions = (
111220
runtime: replaceReference(fnc.runtime, context),
112221
memorySize: replaceReference(fnc.memory, context),
113222
timeout: replaceReference(fnc.timeout, context),
223+
diskSize: fnc.storage?.disk,
114224
environmentVariables: replaceReference(fnc.environment, context),
115225
code,
116226
logConfig,
227+
vpcConfig,
228+
nasConfig: fcNas?.length
229+
? {
230+
mountPoints: fcNas?.map(({ nasMount, mountDir }) => ({
231+
mountDir,
232+
serverAddr: `${nasMount.attrMountTargetDomain}:/`,
233+
})),
234+
}
235+
: undefined,
117236
},
118237
true,
119238
);
@@ -126,5 +245,10 @@ export const resolveFunctions = (
126245
if (storeInBucket) {
127246
fcn.addRosDependency(`${service}_artifacts_code_deployment`);
128247
}
248+
if (fcNas?.length) {
249+
fcNas.forEach((nasItem) => {
250+
fcn.addRosDependency(`${fnc.key}_nas_mount_${encodeBase64ForRosId(nasItem.mountDir)}`);
251+
});
252+
}
129253
});
130254
};

src/types/domains/function.ts

+50-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,57 @@ export type FunctionRaw = {
99
environment?: {
1010
[key: string]: string;
1111
};
12+
network?: {
13+
vpc_id: string;
14+
subnet_ids: Array<string>;
15+
security_group: {
16+
name: string;
17+
ingress: Array<string>;
18+
egress: Array<string>;
19+
};
20+
};
21+
storage?: {
22+
disk?: number;
23+
nas?: Array<{
24+
mount_path: string;
25+
storage_class: string;
26+
}>;
27+
};
1228
};
1329

14-
export type FunctionDomain = FunctionRaw & {
30+
export type FunctionDomain = {
1531
key: string;
32+
name: string;
33+
runtime: string;
34+
handler: string;
35+
code: string;
36+
memory: number;
37+
timeout: number;
38+
log?: boolean;
39+
environment?: {
40+
[key: string]: string;
41+
};
42+
network?: {
43+
vpc_id: string;
44+
subnet_ids: Array<string>;
45+
security_group: {
46+
name: string;
47+
ingress: Array<string>;
48+
egress: Array<string>;
49+
};
50+
};
51+
storage: {
52+
disk?: number;
53+
nas?: Array<{
54+
mount_path: string;
55+
storage_class: NasStorageClassEnum;
56+
}>;
57+
};
1658
};
59+
60+
export enum NasStorageClassEnum {
61+
STANDARD_PERFORMANCE = 'STANDARD_PERFORMANCE',
62+
STANDARD_CAPACITY = 'STANDARD_CAPACITY',
63+
EXTREME_STANDARD = 'EXTREME_STANDARD',
64+
EXTREME_ADVANCE = 'EXTREME_ADVANCE',
65+
}

0 commit comments

Comments
 (0)