Skip to content

Commit 0c87a82

Browse files
authored
refactor: store contexture info into a AsyncLocalStorage (#57)
refactor: store contexture info into a AsyncLocalStorage Refs: #38 --------- Signed-off-by: seven <[email protected]>
1 parent 8aea948 commit 0c87a82

29 files changed

+267
-200
lines changed

src/commands/deploy.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { deployStack } from '../stack';
2-
import { constructActionContext, getIacLocation, logger } from '../common';
2+
import { getIacLocation, logger, setContext } from '../common';
33
import { parseYaml } from '../parser';
44

55
export const deploy = async (
@@ -19,10 +19,10 @@ export const deploy = async (
1919
const iac = parseYaml(getIacLocation(options.location));
2020
logger.info('Yaml is valid! 🎉');
2121

22-
const context = constructActionContext({ ...options, stackName, iacProvider: iac.provider });
22+
setContext({ ...options, stackName, iacProvider: iac.provider });
2323

2424
logger.info('Deploying stack...');
25-
await deployStack(stackName, iac, context);
25+
await deployStack(stackName, iac);
2626

2727
logger.info('Stack deployed! 🎉');
2828
};

src/commands/destroy.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { constructActionContext, getIacLocation, logger, rosStackDelete } from '../common';
1+
import { getContext, getIacLocation, logger, rosStackDelete, setContext } from '../common';
22
import { parseYaml } from '../parser';
33

44
export const destroyStack = async (
@@ -13,7 +13,8 @@ export const destroyStack = async (
1313
},
1414
) => {
1515
const iac = parseYaml(getIacLocation(options.location));
16-
const context = constructActionContext({ stackName, ...options, iacProvider: iac.provider });
16+
setContext({ stackName, ...options, iacProvider: iac.provider });
17+
const context = getContext();
1718
logger.info(
1819
`Destroying stack: ${stackName}, provider: ${context.provider}, region: ${context.region}...`,
1920
);

src/commands/index.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#! /usr/bin/env node
22

33
import { Command } from 'commander';
4-
import { constructActionContext, getVersion, logger } from '../common';
4+
import { getContext, getVersion, logger, setContext } from '../common';
55
import { validate } from './validate';
66
import { deploy } from './deploy';
77
import { template } from './template';
@@ -16,7 +16,8 @@ program
1616
.command('show')
1717
.description('show string')
1818
.action(async (options) => {
19-
const context = constructActionContext({ ...options });
19+
setContext({ ...options });
20+
const context = getContext();
2021
const result = await getIamInfo(context);
2122
console.log('result:', JSON.stringify(result));
2223
});

src/commands/template.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { TemplateFormat } from '../types';
22
import yaml from 'yaml';
33
import { generateStackTemplate } from '../stack/deploy';
4-
import { constructActionContext, getIacLocation, logger } from '../common';
4+
import { getIacLocation, logger, setContext } from '../common';
55
import { parseYaml } from '../parser';
66

77
export const template = (
@@ -10,9 +10,9 @@ export const template = (
1010
) => {
1111
const iac = parseYaml(getIacLocation(options.location));
1212

13-
const context = constructActionContext({ ...options, stackName, provider: iac.provider.name });
13+
setContext({ ...options, stackName, provider: iac.provider.name });
1414

15-
const { template } = generateStackTemplate(stackName, iac, context);
15+
const { template } = generateStackTemplate(stackName, iac);
1616
if (typeof template === 'string') {
1717
logger.info(`\n${template}`);
1818
} else {

src/commands/validate.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { constructActionContext, logger } from '../common';
1+
import { getContext, logger, setContext } from '../common';
22
import { parseYaml } from '../parser';
33

44
export const validate = (
55
stackName: string,
66
options: { location: string | undefined; stage: string | undefined },
77
) => {
8-
const context = constructActionContext({ stackName, ...options });
8+
setContext({ stackName, ...options });
9+
const context = getContext();
910
parseYaml(context.iacLocation);
1011
logger.info('Yaml is valid! 🎉');
1112
};

src/common/actionContext.ts renamed to src/common/context.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import { ActionContext, ServerlessIac } from '../types';
1+
import { Context, ServerlessIac } from '../types';
22
import path from 'node:path';
33
import { ProviderEnum } from './providerEnum';
4+
import { AsyncLocalStorage } from 'node:async_hooks';
5+
6+
const asyncLocalStorage = new AsyncLocalStorage<Context>();
47

58
export const getIacLocation = (location?: string): string => {
69
const projectRoot = path.resolve(process.cwd());
@@ -12,7 +15,7 @@ export const getIacLocation = (location?: string): string => {
1215
path.resolve(projectRoot, 'serverless-insight.yml');
1316
};
1417

15-
export const constructActionContext = (config: {
18+
export const setContext = (config: {
1619
stage?: string;
1720
stackName?: string;
1821
region?: string;
@@ -23,8 +26,8 @@ export const constructActionContext = (config: {
2326
location?: string;
2427
parameters?: { [key: string]: string };
2528
iacProvider?: ServerlessIac['provider'];
26-
}): ActionContext => {
27-
return {
29+
}): void => {
30+
const context = {
2831
stage: config.stage ?? 'default',
2932
stackName: config.stackName ?? '',
3033
provider: (config.provider ?? config.iacProvider?.name ?? ProviderEnum.ALIYUN) as ProviderEnum,
@@ -40,4 +43,14 @@ export const constructActionContext = (config: {
4043
iacLocation: getIacLocation(config.location),
4144
parameters: Object.entries(config.parameters ?? {}).map(([key, value]) => ({ key, value })),
4245
};
46+
47+
asyncLocalStorage.enterWith(context);
48+
};
49+
50+
export const getContext = (): Context => {
51+
const context = asyncLocalStorage.getStore();
52+
if (!context) {
53+
throw new Error('No context found');
54+
}
55+
return context;
4356
};

src/common/iacHelper.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import path from 'node:path';
22
import fs from 'node:fs';
33
import * as ros from '@alicloud/ros-cdk-core';
4-
import { ActionContext } from '../types';
4+
import { Context } from '../types';
55
import * as ossDeployment from '@alicloud/ros-cdk-ossdeployment';
66
import crypto from 'node:crypto';
77

@@ -34,13 +34,13 @@ export const getFileSource = (
3434
return { source, objectKey };
3535
};
3636

37-
const evalCtx = (value: string, ctx: ActionContext): string => {
37+
const evalCtx = (value: string, ctx: Context): string => {
3838
const containsStage = value.match(/\$\{ctx.\w+}/);
3939

4040
return containsStage ? value.replace(/\$\{ctx.stage}/g, ctx.stage) : value;
4141
};
4242

43-
export const replaceReference = <T>(value: T, ctx: ActionContext): T => {
43+
export const replaceReference = <T>(value: T, ctx: Context): T => {
4444
if (typeof value === 'string') {
4545
const matchVar = value.match(/^\$\{vars\.(\w+)}$/);
4646
const containsVar = value.match(/\$\{vars\.(\w+)}/);

src/common/imsClient.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import Ims20190815, * as ims20190815 from '@alicloud/ims20190815';
22
import * as openApi from '@alicloud/openapi-client';
3-
import { ActionContext } from '../types';
3+
import { Context } from '../types';
44

5-
export const getIamInfo = async (context: ActionContext) => {
5+
export const getIamInfo = async (context: Context) => {
66
const imsClient = new Ims20190815(
77
new openApi.Config({
88
accessKeyId: context.accessKeyId,

src/common/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ export * from './providerEnum';
22
export * from './logger';
33
export * from './getVersion';
44
export * from './rosClient';
5-
export * from './actionContext';
5+
export * from './context';
66
export * from './iacHelper';
77
export * from './constants';
88
export * from './imsClient';

src/common/rosAssets.ts

+11-14
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import * as ossDeployment from '@alicloud/ros-cdk-ossdeployment';
44
import path from 'node:path';
55
import JSZip from 'jszip';
66
import { logger } from './logger';
7-
import { ActionContext, CdkAssets } from '../types';
7+
import { CdkAssets } from '../types';
88
import { get, isEmpty } from 'lodash';
99
import OSS from 'ali-oss';
10+
import { getContext } from './context';
1011

1112
const buildAssets = (rootPath: string, relativePath: string): Array<ISource> => {
1213
const location = path.resolve(rootPath, relativePath);
@@ -71,10 +72,11 @@ type ConstructedAsset = {
7172
objectKey: string;
7273
};
7374

74-
export const constructAssets = async (
75-
{ files, rootPath }: CdkAssets,
76-
region: string,
77-
): Promise<Array<ConstructedAsset> | undefined> => {
75+
export const constructAssets = async ({
76+
files,
77+
rootPath,
78+
}: CdkAssets): Promise<Array<ConstructedAsset> | undefined> => {
79+
const { region } = getContext();
7880
const assets = await Promise.all(
7981
Object.entries(files)
8082
.filter(([, fileItem]) => !fileItem.source.path.endsWith('.template.json'))
@@ -112,15 +114,13 @@ const ensureBucketExits = async (bucketName: string, ossClient: OSS) =>
112114
}
113115
});
114116

115-
export const publishAssets = async (
116-
assets: Array<ConstructedAsset> | undefined,
117-
context: ActionContext,
118-
) => {
117+
export const publishAssets = async (assets: Array<ConstructedAsset> | undefined) => {
119118
if (!assets?.length) {
120119
logger.info('No assets to publish, skipped!');
121120
return;
122121
}
123122

123+
const context = getContext();
124124
const bucketName = assets[0].bucketName;
125125

126126
const client = new OSS({
@@ -148,15 +148,12 @@ export const publishAssets = async (
148148
return bucketName;
149149
};
150150

151-
export const cleanupAssets = async (
152-
assets: Array<ConstructedAsset> | undefined,
153-
context: ActionContext,
154-
) => {
151+
export const cleanupAssets = async (assets: Array<ConstructedAsset> | undefined) => {
155152
if (!assets?.length) {
156153
logger.info('No assets to cleanup, skipped!');
157154
return;
158155
}
159-
156+
const context = getContext();
160157
const bucketName = assets[0].bucketName;
161158

162159
const client = new OSS({

src/common/rosClient.ts

+8-9
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import ROS20190910, {
1010
UpdateStackRequestParameters,
1111
} from '@alicloud/ros20190910';
1212
import { Config } from '@alicloud/openapi-client';
13-
import { ActionContext } from '../types';
13+
import { Context } from '../types';
1414
import { logger } from './logger';
1515
import { lang } from '../lang';
16+
import { getContext } from './context';
1617

1718
const client = new ROS20190910(
1819
new Config({
@@ -23,7 +24,7 @@ const client = new ROS20190910(
2324
}),
2425
);
2526

26-
const createStack = async (stackName: string, templateBody: unknown, context: ActionContext) => {
27+
const createStack = async (stackName: string, templateBody: unknown, context: Context) => {
2728
const parameters = context.parameters?.map(
2829
(parameter) =>
2930
new CreateStackRequestParameters({
@@ -47,7 +48,7 @@ const createStack = async (stackName: string, templateBody: unknown, context: Ac
4748
return await getStackActionResult(response.body?.stackId || '', context.region);
4849
};
4950

50-
const updateStack = async (stackId: string, templateBody: unknown, context: ActionContext) => {
51+
const updateStack = async (stackId: string, templateBody: unknown, context: Context) => {
5152
const parameters = context.parameters?.map(
5253
(parameter) =>
5354
new UpdateStackRequestParameters({
@@ -156,11 +157,8 @@ const getStackActionResult = async (
156157
});
157158
};
158159

159-
export const rosStackDeploy = async (
160-
stackName: string,
161-
templateBody: unknown,
162-
context: ActionContext,
163-
) => {
160+
export const rosStackDeploy = async (stackName: string, templateBody: unknown) => {
161+
const context = getContext();
164162
const stackInfo = await getStackByName(stackName, context.region);
165163
if (stackInfo) {
166164
const { Status: stackStatus } = stackInfo;
@@ -184,8 +182,9 @@ export const rosStackDeploy = async (
184182
export const rosStackDelete = async ({
185183
stackName,
186184
region,
187-
}: Pick<ActionContext, 'stackName' | 'region' | 'provider'>) => {
185+
}: Pick<Context, 'stackName' | 'region' | 'provider'>) => {
188186
const stackInfo = await getStackByName(stackName, region);
187+
189188
if (!stackInfo) {
190189
logger.warn(`Stack: ${stackName} not exists, skipped! 🚫`);
191190
return;

src/stack/deploy.ts

+15-26
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import * as ros from '@alicloud/ros-cdk-core';
22
import fs from 'node:fs';
33

4-
import { ActionContext, ServerlessIac } from '../types';
4+
import { ServerlessIac } from '../types';
55
import {
66
cleanupAssets,
77
constructAssets,
8+
getContext,
89
logger,
910
ProviderEnum,
1011
publishAssets,
@@ -14,11 +15,8 @@ import { prepareBootstrapStack, RosStack } from './rosStack';
1415
import { RfsStack } from './rfsStack';
1516
import { get } from 'lodash';
1617

17-
export const generateRosStackTemplate = (
18-
stackName: string,
19-
iac: ServerlessIac,
20-
context: ActionContext,
21-
) => {
18+
export const generateRosStackTemplate = (stackName: string, iac: ServerlessIac) => {
19+
const context = getContext();
2220
const app = new ros.App();
2321
new RosStack(app, iac, context);
2422

@@ -36,39 +34,31 @@ export const generateRosStackTemplate = (
3634
return { template, assets };
3735
};
3836

39-
export const generateRfsStackTemplate = (
40-
stackName: string,
41-
iac: ServerlessIac,
42-
context: ActionContext,
43-
) => {
44-
const stack = new RfsStack(iac, context);
37+
export const generateRfsStackTemplate = (stackName: string, iac: ServerlessIac) => {
38+
const stack = new RfsStack(iac);
4539

4640
const hcl = stack.toHclTerraform();
4741
console.log('HCL:', hcl);
4842

4943
return { template: hcl };
5044
};
5145

52-
export const deployStack = async (
53-
stackName: string,
54-
iac: ServerlessIac,
55-
context: ActionContext,
56-
) => {
57-
const { template, assets } = generateRosStackTemplate(stackName, iac, context);
58-
await prepareBootstrapStack(context);
46+
export const deployStack = async (stackName: string, iac: ServerlessIac) => {
47+
const { template, assets } = generateRosStackTemplate(stackName, iac);
48+
await prepareBootstrapStack();
5949
logger.info(`Deploying stack, publishing assets...`);
60-
const constructedAssets = await constructAssets(assets, context.region);
50+
const constructedAssets = await constructAssets(assets);
6151
try {
62-
await publishAssets(constructedAssets, context);
52+
await publishAssets(constructedAssets);
6353
logger.info(`Assets published! 🎉`);
64-
await rosStackDeploy(stackName, template, context);
54+
await rosStackDeploy(stackName, template);
6555
} catch (e) {
6656
logger.error(`Failed to deploy stack: ${e}`);
6757
throw e;
6858
} finally {
6959
try {
7060
logger.info(`Cleaning up temporary Assets...`);
71-
await cleanupAssets(constructedAssets, context);
61+
await cleanupAssets(constructedAssets);
7262
logger.info(`Assets cleaned up!♻️`);
7363
} catch (e) {
7464
logger.error(
@@ -81,12 +71,11 @@ export const deployStack = async (
8171
export const generateStackTemplate = (
8272
stackName: string,
8373
iac: ServerlessIac,
84-
context: ActionContext,
8574
): { template: unknown } => {
8675
if (iac.provider.name === ProviderEnum.ALIYUN) {
87-
return generateRosStackTemplate(stackName, iac, context);
76+
return generateRosStackTemplate(stackName, iac);
8877
} else if (iac.provider.name === ProviderEnum.HUAWEI) {
89-
return generateRfsStackTemplate(stackName, iac, context);
78+
return generateRfsStackTemplate(stackName, iac);
9079
}
9180
return { template: '' };
9281
};

0 commit comments

Comments
 (0)