Skip to content

Commit 5b5b481

Browse files
committed
feat: implement parameters #5
Signed-off-by: seven <zilisheng1996@gmail.com>
1 parent 9e093da commit 5b5b481

File tree

3 files changed

+189
-155
lines changed

3 files changed

+189
-155
lines changed

src/stack/deploy.ts

Lines changed: 2 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,160 +1,7 @@
11
import * as ros from '@alicloud/ros-cdk-core';
2-
import { RosParameterType } from '@alicloud/ros-cdk-core';
3-
import * as fc from '@alicloud/ros-cdk-fc';
4-
import * as agw from '@alicloud/ros-cdk-apigateway';
5-
import * as ram from '@alicloud/ros-cdk-ram';
6-
7-
import { ActionContext, EventTypes, ServerlessIac } from '../types';
2+
import { ActionContext, ServerlessIac } from '../types';
83
import { printer, rosStackDeploy } from '../common';
9-
import path from 'node:path';
10-
import * as fs from 'node:fs';
11-
12-
const resolveCode = (location: string): string => {
13-
const filePath = path.resolve(process.cwd(), location);
14-
const fileContent = fs.readFileSync(filePath);
15-
16-
return fileContent.toString('base64');
17-
};
18-
19-
export class IacStack extends ros.Stack {
20-
constructor(scope: ros.Construct, iac: ServerlessIac, context: ActionContext) {
21-
super(scope, iac.service, {
22-
tags: iac.tags.reduce((acc: { [key: string]: string }, tag) => {
23-
acc[tag.key] = tag.value;
24-
return acc;
25-
}, {}),
26-
});
27-
28-
Object.entries(iac.vars).map(
29-
([key, value]) =>
30-
new ros.RosParameter(this, key, {
31-
type: RosParameterType.STRING,
32-
defaultValue: value,
33-
}),
34-
);
35-
36-
new ros.RosInfo(this, ros.RosInfo.description, `${iac.service} stack`);
37-
38-
const service = new fc.RosService(
39-
this,
40-
`${iac.service}-service`,
41-
{
42-
serviceName: `${iac.service}-service`,
43-
tags: iac.tags,
44-
},
45-
true,
46-
);
47-
48-
iac.functions.forEach((fnc) => {
49-
const func = new fc.RosFunction(
50-
this,
51-
fnc.key,
52-
{
53-
functionName: fnc.name,
54-
serviceName: service.serviceName,
55-
handler: fnc.handler,
56-
runtime: fnc.runtime,
57-
memorySize: fnc.memory,
58-
timeout: fnc.timeout,
59-
environmentVariables: fnc.environment,
60-
code: {
61-
zipFile: resolveCode(fnc.code),
62-
},
63-
},
64-
true,
65-
);
66-
func.addDependsOn(service);
67-
});
68-
69-
const apiGateway = iac.events.find((event) => event.type === EventTypes.API_GATEWAY);
70-
if (apiGateway) {
71-
const gatewayAccessRole = new ram.RosRole(
72-
this,
73-
`${iac.service}_role`,
74-
{
75-
roleName: `${iac.service}-gateway-access-role`,
76-
description: `${iac.service} role`,
77-
assumeRolePolicyDocument: {
78-
version: '1',
79-
statement: [
80-
{
81-
action: 'sts:AssumeRole',
82-
effect: 'Allow',
83-
principal: {
84-
service: ['apigateway.aliyuncs.com'],
85-
},
86-
},
87-
],
88-
},
89-
policies: [
90-
{
91-
policyName: `${iac.service}-policy`,
92-
policyDocument: {
93-
version: '1',
94-
statement: [
95-
{
96-
action: ['fc:InvokeFunction'],
97-
effect: 'Allow',
98-
// @TODO implement at least permission granting
99-
resource: ['*'],
100-
},
101-
],
102-
},
103-
},
104-
],
105-
},
106-
true,
107-
);
108-
109-
const apiGatewayGroup = new agw.RosGroup(
110-
this,
111-
`${iac.service}_apigroup`,
112-
{
113-
groupName: `${iac.service}_apigroup`,
114-
tags: iac.tags,
115-
},
116-
true,
117-
);
118-
119-
iac.events
120-
.filter((event) => event.type === EventTypes.API_GATEWAY)
121-
.forEach((event) => {
122-
event.triggers.forEach((trigger) => {
123-
const key = `${trigger.method}_${trigger.path}`.toLowerCase().replace(/\//g, '_');
124-
const api = new agw.RosApi(
125-
this,
126-
`${event.key}_api_${key}`,
127-
{
128-
apiName: `${event.name}_api_${key}`,
129-
groupId: apiGatewayGroup.attrGroupId,
130-
visibility: 'PRIVATE',
131-
requestConfig: {
132-
requestProtocol: 'HTTP',
133-
requestHttpMethod: trigger.method,
134-
requestPath: trigger.path,
135-
requestMode: 'PASSTHROUGH',
136-
},
137-
serviceConfig: {
138-
serviceProtocol: 'FunctionCompute',
139-
functionComputeConfig: {
140-
fcRegionId: context.region,
141-
serviceName: service.serviceName,
142-
functionName: trigger.backend,
143-
roleArn: gatewayAccessRole.attrArn,
144-
},
145-
},
146-
resultSample: 'ServerlessInsight resultSample',
147-
resultType: 'JSON',
148-
tags: iac.tags,
149-
},
150-
true,
151-
);
152-
api.addDependsOn(apiGatewayGroup);
153-
});
154-
});
155-
}
156-
}
157-
}
4+
import { IacStack } from './iacStack';
1585

1596
const generateStackTemplate = (stackName: string, iac: ServerlessIac, context: ActionContext) => {
1607
const app = new ros.App();

src/stack/iacStack.ts

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import * as ros from '@alicloud/ros-cdk-core';
2+
import { ActionContext, EventTypes, ServerlessIac } from '../types';
3+
import { RosParameterType } from '@alicloud/ros-cdk-core';
4+
import * as fc from '@alicloud/ros-cdk-fc';
5+
import * as ram from '@alicloud/ros-cdk-ram';
6+
import * as agw from '@alicloud/ros-cdk-apigateway';
7+
import path from 'node:path';
8+
import fs from 'node:fs';
9+
10+
const resolveCode = (location: string): string => {
11+
const filePath = path.resolve(process.cwd(), location);
12+
const fileContent = fs.readFileSync(filePath);
13+
14+
return fileContent.toString('base64');
15+
};
16+
17+
const replaceVars = (
18+
value: { [key: string]: unknown } | string | number | unknown,
19+
): { [key: string]: unknown } | string | number | unknown => {
20+
if (typeof value === 'string') {
21+
const matchVar = value.match(/^\$\{vars\.(\w+)}$/);
22+
const containsVar = value.match(/\$\{vars\.(\w+)}/);
23+
if (matchVar?.length) {
24+
return ros.Fn.ref(matchVar[1]);
25+
}
26+
if (containsVar?.length) {
27+
return ros.Fn.sub(value.replace(/\$\{vars\.(\w+)}/g, '${$1}'));
28+
}
29+
return value;
30+
}
31+
32+
if (Array.isArray(value)) {
33+
return value.map(replaceVars);
34+
}
35+
36+
if (typeof value === 'object' && value !== null) {
37+
return Object.fromEntries(Object.entries(value).map(([key, val]) => [key, replaceVars(val)]));
38+
}
39+
40+
return value;
41+
};
42+
43+
export class IacStack extends ros.Stack {
44+
private readonly iac: ServerlessIac;
45+
46+
constructor(scope: ros.Construct, iac: ServerlessIac, context: ActionContext) {
47+
super(scope, iac.service, {
48+
tags: iac.tags.reduce((acc: { [key: string]: string }, tag) => {
49+
acc[tag.key] = tag.value;
50+
return acc;
51+
}, {}),
52+
});
53+
54+
this.iac = replaceVars(iac) as ServerlessIac;
55+
console.log('IAC:', JSON.stringify(this.iac));
56+
57+
Object.entries(this.iac.vars).map(
58+
([key, value]) =>
59+
new ros.RosParameter(this, key, {
60+
type: RosParameterType.STRING,
61+
defaultValue: value,
62+
}),
63+
);
64+
65+
new ros.RosInfo(this, ros.RosInfo.description, `${this.iac.service} stack`);
66+
67+
const service = new fc.RosService(
68+
this,
69+
`${this.iac.service}-service`,
70+
{
71+
serviceName: `${this.iac.service}-service`,
72+
tags: this.iac.tags,
73+
},
74+
true,
75+
);
76+
77+
this.iac.functions.forEach((fnc) => {
78+
const func = new fc.RosFunction(
79+
this,
80+
fnc.key,
81+
{
82+
functionName: fnc.name,
83+
serviceName: service.serviceName,
84+
handler: fnc.handler,
85+
runtime: fnc.runtime,
86+
memorySize: fnc.memory,
87+
timeout: fnc.timeout,
88+
environmentVariables: fnc.environment,
89+
code: {
90+
zipFile: resolveCode(fnc.code),
91+
},
92+
},
93+
true,
94+
);
95+
func.addDependsOn(service);
96+
});
97+
98+
const apiGateway = this.iac.events.find((event) => event.type === EventTypes.API_GATEWAY);
99+
if (apiGateway) {
100+
const gatewayAccessRole = new ram.RosRole(
101+
this,
102+
`${this.iac.service}_role`,
103+
{
104+
roleName: `${this.iac.service}-gateway-access-role`,
105+
description: `${this.iac.service} role`,
106+
assumeRolePolicyDocument: {
107+
version: '1',
108+
statement: [
109+
{
110+
action: 'sts:AssumeRole',
111+
effect: 'Allow',
112+
principal: {
113+
service: ['apigateway.aliyuncs.com'],
114+
},
115+
},
116+
],
117+
},
118+
policies: [
119+
{
120+
policyName: `${iac.service}-policy`,
121+
policyDocument: {
122+
version: '1',
123+
statement: [
124+
{
125+
action: ['fc:InvokeFunction'],
126+
effect: 'Allow',
127+
// @TODO implement at least permission granting
128+
resource: ['*'],
129+
},
130+
],
131+
},
132+
},
133+
],
134+
},
135+
true,
136+
);
137+
138+
const apiGatewayGroup = new agw.RosGroup(
139+
this,
140+
`${this.iac.service}_apigroup`,
141+
{
142+
groupName: `${this.iac.service}_apigroup`,
143+
tags: this.iac.tags,
144+
},
145+
true,
146+
);
147+
148+
this.iac.events
149+
.filter((event) => event.type === EventTypes.API_GATEWAY)
150+
.forEach((event) => {
151+
event.triggers.forEach((trigger) => {
152+
const key = `${trigger.method}_${trigger.path}`.toLowerCase().replace(/\//g, '_');
153+
const api = new agw.RosApi(
154+
this,
155+
`${event.key}_api_${key}`,
156+
{
157+
apiName: `${event.name}_api_${key}`,
158+
groupId: apiGatewayGroup.attrGroupId,
159+
visibility: 'PRIVATE',
160+
requestConfig: {
161+
requestProtocol: 'HTTP',
162+
requestHttpMethod: trigger.method,
163+
requestPath: trigger.path,
164+
requestMode: 'PASSTHROUGH',
165+
},
166+
serviceConfig: {
167+
serviceProtocol: 'FunctionCompute',
168+
functionComputeConfig: {
169+
fcRegionId: context.region,
170+
serviceName: service.serviceName,
171+
functionName: trigger.backend,
172+
roleArn: gatewayAccessRole.attrArn,
173+
},
174+
},
175+
resultSample: 'ServerlessInsight resultSample',
176+
resultType: 'JSON',
177+
tags: iac.tags,
178+
},
179+
true,
180+
);
181+
api.addDependsOn(apiGatewayGroup);
182+
});
183+
});
184+
}
185+
}
186+
}

tests/fixtures/serverless-insignt.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ functions:
2626
environment:
2727
NODE_ENV: production
2828
TEST_VAR: ${vars.testv}
29+
TEST_VAR_EXTRA: abcds-${vars.testv}-andyou
2930

3031

3132
events:

0 commit comments

Comments
 (0)