Skip to content

Commit cd0a587

Browse files
committed
feat: deployment action calling aliyun ok
Signed-off-by: seven <zilisheng1996@gmail.com>
1 parent e33b209 commit cd0a587

File tree

11 files changed

+513
-28
lines changed

11 files changed

+513
-28
lines changed

package-lock.json

Lines changed: 328 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,10 @@
4949
"function"
5050
],
5151
"dependencies": {
52+
"@alicloud/openapi-client": "^0.4.11",
5253
"@alicloud/ros-cdk-core": "^1.2.0",
5354
"@alicloud/ros-cdk-fc": "^1.2.0",
55+
"@alicloud/ros20190910": "^3.4.3",
5456
"ajv": "^8.17.1",
5557
"chalk": "^4.1.2",
5658
"commander": "^11.1.0",

src/commands/deploy.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { deployStack } from '../stack';
22
import { printer } from '../common';
33
import { parseYaml } from '../iac';
4+
import { constructActionContext } from '../common/actionContext';
45

5-
export const deploy = (location?: string) => {
6+
export const deploy = async (stackName: string, options: { location: string }) => {
7+
const context = constructActionContext({ location: options.location });
8+
printer.info(`Deploying stack context: ${JSON.stringify(context)}...`);
69
printer.info('Validating yaml...');
7-
const iac = parseYaml(location);
10+
const iac = parseYaml(context.iacLocation);
811
printer.success('Yaml is valid! 🎉');
912

1013
printer.info('Deploying stack...');
11-
deployStack(iac);
14+
await deployStack(stackName, iac, context);
15+
1216
printer.success('Stack deployed! 🎉');
1317
};

src/commands/index.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,21 @@ program
2323
});
2424

2525
program
26-
.command('validate [location]')
26+
.command('validate [stackName]')
2727
.description('validate serverless Iac yaml')
28-
.action((location) => {
28+
.option('-f, --file <path>', 'specify the yaml file')
29+
.action((stackName, options) => {
2930
logger.debug('log command info');
30-
validate(location);
31+
validate(options.file);
3132
});
3233

3334
program
34-
.command('deploy [location]')
35+
.command('deploy <stackName>')
3536
.description('deploy serverless Iac yaml')
36-
.action((location) => {
37+
.option('-f, --file <path>', 'specify the yaml file')
38+
.action(async (stackName, options) => {
3739
logger.debug('log command info');
38-
deploy(location);
40+
await deploy(stackName, { location: options.file });
3941
});
4042

4143
program.parse();

src/commands/validate.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { parseYaml } from '../iac';
22
import { printer } from '../common';
3+
import { constructActionContext } from '../common/actionContext';
34

45
export const validate = (location?: string) => {
5-
parseYaml(location);
6+
const context = constructActionContext({ location });
7+
parseYaml(context.iacLocation);
68
printer.success('Yaml is valid! 🎉');
79
};

src/common/actionContext.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { ActionContext } from '../types';
2+
import path from 'node:path';
3+
4+
export const constructActionContext = (config?: {
5+
region?: string;
6+
account?: string;
7+
accessKeyId?: string;
8+
accessKeySecret?: string;
9+
securityToken?: string;
10+
location?: string;
11+
}): ActionContext => {
12+
return {
13+
region:
14+
config?.region ?? process.env.ROS_REGION_ID ?? process.env.ALIYUN_REGION ?? 'cn-hangzhou',
15+
accessKeyId: config?.accessKeyId ?? (process.env.ALIYUN_ACCESS_KEY_ID as string),
16+
accessKeySecret: config?.accessKeySecret ?? (process.env.ALIYUN_ACCESS_KEY_SECRET as string),
17+
securityToken: config?.securityToken ?? process.env.ALIYUN_SECURITY_TOKEN,
18+
iacLocation: (() => {
19+
const projectRoot = path.resolve(process.cwd());
20+
return config?.location
21+
? path.resolve(projectRoot, config?.location)
22+
: path.resolve(projectRoot, 'serverless-insight.yml');
23+
})(),
24+
};
25+
};

src/common/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from './printer';
22
export * from './provider';
33
export * from './logger';
44
export * from './getVersion';
5+
export * from './rosClient';

src/common/rosClient.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import Util from '@alicloud/tea-util';
2+
import ROS20190910, {
3+
CreateStackRequest,
4+
CreateStackRequestParameters,
5+
CreateStackRequestTags,
6+
ListStacksRequest,
7+
} from '@alicloud/ros20190910';
8+
import { Config } from '@alicloud/openapi-client';
9+
import { ActionContext } from '../types';
10+
import { printer } from './printer';
11+
12+
const client = new ROS20190910(
13+
new Config({
14+
accessKeyId: process.env.ALIYUN_ACCESS_KEY_ID,
15+
accessKeySecret: process.env.ALIYUN_ACCESS_KEY_SECRET,
16+
regionId: process.env.ALIYUN_REGION,
17+
disableRollback: false,
18+
}),
19+
);
20+
21+
const createStack = async (stackName: string, templateBody: unknown, context: ActionContext) => {
22+
const parameters = context.parameters?.map(
23+
(parameter) =>
24+
new CreateStackRequestParameters({
25+
parameterKey: Util.assertAsString(parameter.key),
26+
parameterValue: Util.assertAsString(parameter.value),
27+
}),
28+
);
29+
30+
const createStackRequest = new CreateStackRequest({
31+
regionId: context.region,
32+
stackName,
33+
templateBody: JSON.stringify(templateBody),
34+
parameters,
35+
tags: context.tags?.map((tag) => new CreateStackRequestTags(tag)),
36+
});
37+
38+
console.log('createStackRequest:', createStackRequest);
39+
40+
const response = await client.createStack(createStackRequest);
41+
console.log(`创建中,资源栈ID:${response.body?.stackId}`);
42+
return response.body?.stackId;
43+
};
44+
45+
const updateStack = async (stackName: string, templateBody: unknown, context: ActionContext) => {
46+
const parameters = context.parameters?.map(
47+
(parameter) =>
48+
new CreateStackRequestParameters({
49+
parameterKey: Util.assertAsString(parameter.key),
50+
parameterValue: Util.assertAsString(parameter.value),
51+
}),
52+
);
53+
54+
const createStackRequest = new CreateStackRequest({
55+
stackName,
56+
templateBody,
57+
parameters,
58+
});
59+
60+
const response = await client.updateStack(createStackRequest);
61+
console.log(`更新中,资源栈ID:${response.body?.stackId}`);
62+
return response.body?.stackId;
63+
};
64+
65+
const getStackByName = async (stackName: string, region: string) => {
66+
const result = await client.listStacks(
67+
new ListStacksRequest({
68+
regionId: region,
69+
pageSize: 10,
70+
pageNumber: 1,
71+
stackName: [stackName],
72+
}),
73+
);
74+
75+
if (result.statusCode === 200) {
76+
return result.body?.stacks?.[0];
77+
} else {
78+
return null;
79+
}
80+
};
81+
82+
export const rosStackDeploy = async (
83+
stackName: string,
84+
templateBody: unknown,
85+
context: ActionContext,
86+
) => {
87+
const stackInfo = await getStackByName(stackName, context.region);
88+
if (stackInfo) {
89+
const { Status: stackStatus } = stackInfo;
90+
if (stackStatus?.indexOf('IN_PROGRESS') >= 0) {
91+
printer.error(`fail to update stack, because stack status is ${stackStatus}`);
92+
throw new Error(`fail to update stack, because stack status is ${stackStatus}`);
93+
}
94+
95+
printer.info(`Update stack: ${stackName} deploying... `);
96+
return await updateStack(stackName, templateBody, context);
97+
} else {
98+
// create stack
99+
printer.info(`Create stack: ${stackName} deploying... `);
100+
return await createStack(stackName, templateBody, context);
101+
}
102+
};

src/iac/parse.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { parse } from 'yaml';
22
import { existsSync, readFileSync } from 'node:fs';
3-
import { IacFunction, RawServerlessIac, ServerlessIac, Event } from '../types';
3+
import { Event, IacFunction, RawServerlessIac, ServerlessIac } from '../types';
44
import { validateYaml } from './iacSchema';
5-
import path from 'node:path';
65

76
const mapToArr = (obj: Record<string, Record<string, unknown> | string>) => {
87
return Object.entries(obj).map(([key, value]) =>
@@ -29,12 +28,7 @@ const transformYaml = (iacJson: RawServerlessIac): ServerlessIac => {
2928
};
3029
};
3130

32-
export const parseYaml = (location?: string): ServerlessIac => {
33-
const projectRoot = path.resolve(process.cwd());
34-
const yamlPath = location
35-
? path.resolve(projectRoot, location)
36-
: path.resolve(projectRoot, 'serverless-insight.yml');
37-
31+
export const parseYaml = (yamlPath: string): ServerlessIac => {
3832
validateExistence(yamlPath);
3933

4034
const yamlContent = readFileSync(yamlPath, 'utf8');

src/stack/deploy.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as ros from '@alicloud/ros-cdk-core';
22
import * as fc from '@alicloud/ros-cdk-fc';
3-
import { ServerlessIac } from '../types';
3+
import { ActionContext, ServerlessIac } from '../types';
4+
import { printer, rosStackDeploy } from '../common';
45

56
export class IacStack extends ros.Stack {
67
constructor(scope: ros.Construct, iac: ServerlessIac, props?: ros.StackProps) {
@@ -30,9 +31,29 @@ export class IacStack extends ros.Stack {
3031
}
3132
}
3233

33-
export const deployStack = (iac: ServerlessIac) => {
34-
console.log(`Deploying stack... ${JSON.stringify(iac)}`);
34+
const generateStackTemplate = (stackName: string, iac: ServerlessIac) => {
3535
const app = new ros.App();
3636
new IacStack(app, iac);
37-
app.synth();
37+
38+
const assembly = app.synth();
39+
const stackArtifact = assembly.getStackByName(stackName);
40+
const parameters = Object.entries(stackArtifact.parameters).map(([key, value]) => ({
41+
key,
42+
value,
43+
}));
44+
45+
return { template: stackArtifact.template, parameters };
46+
};
47+
48+
export const deployStack = async (
49+
stackName: string,
50+
iac: ServerlessIac,
51+
context: ActionContext,
52+
) => {
53+
printer.info(`Deploying stack... ${JSON.stringify(iac)}`);
54+
55+
const { template, parameters } = generateStackTemplate(stackName, iac);
56+
console.log('Generated ROS YAML:', JSON.stringify({ template, parameters }));
57+
await rosStackDeploy(stackName, template, { ...context, parameters });
58+
printer.info(`Stack deployed! 🎉`);
3859
};

0 commit comments

Comments
 (0)