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
6 changes: 3 additions & 3 deletions src/commands/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { deployStack } from '../stack';
import { constructActionContext, getIacLocation, logger } from '../common';
import { getIacLocation, logger, setContext } from '../common';
import { parseYaml } from '../parser';

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

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

logger.info('Deploying stack...');
await deployStack(stackName, iac, context);
await deployStack(stackName, iac);

logger.info('Stack deployed! 🎉');
};
5 changes: 3 additions & 2 deletions src/commands/destroy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { constructActionContext, getIacLocation, logger, rosStackDelete } from '../common';
import { getContext, getIacLocation, logger, rosStackDelete, setContext } from '../common';
import { parseYaml } from '../parser';

export const destroyStack = async (
Expand All @@ -13,7 +13,8 @@ export const destroyStack = async (
},
) => {
const iac = parseYaml(getIacLocation(options.location));
const context = constructActionContext({ stackName, ...options, iacProvider: iac.provider });
setContext({ stackName, ...options, iacProvider: iac.provider });
const context = getContext();
logger.info(
`Destroying stack: ${stackName}, provider: ${context.provider}, region: ${context.region}...`,
);
Expand Down
5 changes: 3 additions & 2 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#! /usr/bin/env node

import { Command } from 'commander';
import { constructActionContext, getVersion, logger } from '../common';
import { getContext, getVersion, logger, setContext } from '../common';
import { validate } from './validate';
import { deploy } from './deploy';
import { template } from './template';
Expand All @@ -16,7 +16,8 @@ program
.command('show')
.description('show string')
.action(async (options) => {
const context = constructActionContext({ ...options });
setContext({ ...options });
const context = getContext();
const result = await getIamInfo(context);
console.log('result:', JSON.stringify(result));
});
Expand Down
6 changes: 3 additions & 3 deletions src/commands/template.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TemplateFormat } from '../types';
import yaml from 'yaml';
import { generateStackTemplate } from '../stack/deploy';
import { constructActionContext, getIacLocation, logger } from '../common';
import { getIacLocation, logger, setContext } from '../common';
import { parseYaml } from '../parser';

export const template = (
Expand All @@ -10,9 +10,9 @@ export const template = (
) => {
const iac = parseYaml(getIacLocation(options.location));

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

const { template } = generateStackTemplate(stackName, iac, context);
const { template } = generateStackTemplate(stackName, iac);
if (typeof template === 'string') {
logger.info(`\n${template}`);
} else {
Expand Down
5 changes: 3 additions & 2 deletions src/commands/validate.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { constructActionContext, logger } from '../common';
import { getContext, logger, setContext } from '../common';
import { parseYaml } from '../parser';

export const validate = (
stackName: string,
options: { location: string | undefined; stage: string | undefined },
) => {
const context = constructActionContext({ stackName, ...options });
setContext({ stackName, ...options });
const context = getContext();
parseYaml(context.iacLocation);
logger.info('Yaml is valid! 🎉');
};
21 changes: 17 additions & 4 deletions src/common/actionContext.ts → src/common/context.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { ActionContext, ServerlessIac } from '../types';
import { Context, ServerlessIac } from '../types';
import path from 'node:path';
import { ProviderEnum } from './providerEnum';
import { AsyncLocalStorage } from 'node:async_hooks';

const asyncLocalStorage = new AsyncLocalStorage<Context>();

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

export const constructActionContext = (config: {
export const setContext = (config: {
stage?: string;
stackName?: string;
region?: string;
Expand All @@ -23,8 +26,8 @@
location?: string;
parameters?: { [key: string]: string };
iacProvider?: ServerlessIac['provider'];
}): ActionContext => {
return {
}): void => {
const context = {
stage: config.stage ?? 'default',
stackName: config.stackName ?? '',
provider: (config.provider ?? config.iacProvider?.name ?? ProviderEnum.ALIYUN) as ProviderEnum,
Expand All @@ -40,4 +43,14 @@
iacLocation: getIacLocation(config.location),
parameters: Object.entries(config.parameters ?? {}).map(([key, value]) => ({ key, value })),
};

asyncLocalStorage.enterWith(context);
};

export const getContext = (): Context => {
const context = asyncLocalStorage.getStore();
if (!context) {
throw new Error('No context found');

Check warning on line 53 in src/common/context.ts

View check run for this annotation

Codecov / codecov/patch

src/common/context.ts#L53

Added line #L53 was not covered by tests
}
return context;
};
6 changes: 3 additions & 3 deletions src/common/iacHelper.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from 'node:path';
import fs from 'node:fs';
import * as ros from '@alicloud/ros-cdk-core';
import { ActionContext } from '../types';
import { Context } from '../types';
import * as ossDeployment from '@alicloud/ros-cdk-ossdeployment';
import crypto from 'node:crypto';

Expand Down Expand Up @@ -34,13 +34,13 @@ export const getFileSource = (
return { source, objectKey };
};

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

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

export const replaceReference = <T>(value: T, ctx: ActionContext): T => {
export const replaceReference = <T>(value: T, ctx: Context): T => {
if (typeof value === 'string') {
const matchVar = value.match(/^\$\{vars\.(\w+)}$/);
const containsVar = value.match(/\$\{vars\.(\w+)}/);
Expand Down
4 changes: 2 additions & 2 deletions src/common/imsClient.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Ims20190815, * as ims20190815 from '@alicloud/ims20190815';
import * as openApi from '@alicloud/openapi-client';
import { ActionContext } from '../types';
import { Context } from '../types';

export const getIamInfo = async (context: ActionContext) => {
export const getIamInfo = async (context: Context) => {
const imsClient = new Ims20190815(
new openApi.Config({
accessKeyId: context.accessKeyId,
Expand Down
2 changes: 1 addition & 1 deletion src/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export * from './providerEnum';
export * from './logger';
export * from './getVersion';
export * from './rosClient';
export * from './actionContext';
export * from './context';
export * from './iacHelper';
export * from './constants';
export * from './imsClient';
Expand Down
25 changes: 11 additions & 14 deletions src/common/rosAssets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import * as ossDeployment from '@alicloud/ros-cdk-ossdeployment';
import path from 'node:path';
import JSZip from 'jszip';
import { logger } from './logger';
import { ActionContext, CdkAssets } from '../types';
import { CdkAssets } from '../types';
import { get, isEmpty } from 'lodash';
import OSS from 'ali-oss';
import { getContext } from './context';

const buildAssets = (rootPath: string, relativePath: string): Array<ISource> => {
const location = path.resolve(rootPath, relativePath);
Expand Down Expand Up @@ -71,10 +72,11 @@ type ConstructedAsset = {
objectKey: string;
};

export const constructAssets = async (
{ files, rootPath }: CdkAssets,
region: string,
): Promise<Array<ConstructedAsset> | undefined> => {
export const constructAssets = async ({
files,
rootPath,
}: CdkAssets): Promise<Array<ConstructedAsset> | undefined> => {
const { region } = getContext();
const assets = await Promise.all(
Object.entries(files)
.filter(([, fileItem]) => !fileItem.source.path.endsWith('.template.json'))
Expand Down Expand Up @@ -112,15 +114,13 @@ const ensureBucketExits = async (bucketName: string, ossClient: OSS) =>
}
});

export const publishAssets = async (
assets: Array<ConstructedAsset> | undefined,
context: ActionContext,
) => {
export const publishAssets = async (assets: Array<ConstructedAsset> | undefined) => {
if (!assets?.length) {
logger.info('No assets to publish, skipped!');
return;
}

const context = getContext();
const bucketName = assets[0].bucketName;

const client = new OSS({
Expand Down Expand Up @@ -148,15 +148,12 @@ export const publishAssets = async (
return bucketName;
};

export const cleanupAssets = async (
assets: Array<ConstructedAsset> | undefined,
context: ActionContext,
) => {
export const cleanupAssets = async (assets: Array<ConstructedAsset> | undefined) => {
if (!assets?.length) {
logger.info('No assets to cleanup, skipped!');
return;
}

const context = getContext();
const bucketName = assets[0].bucketName;

const client = new OSS({
Expand Down
17 changes: 8 additions & 9 deletions src/common/rosClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import ROS20190910, {
UpdateStackRequestParameters,
} from '@alicloud/ros20190910';
import { Config } from '@alicloud/openapi-client';
import { ActionContext } from '../types';
import { Context } from '../types';
import { logger } from './logger';
import { lang } from '../lang';
import { getContext } from './context';

const client = new ROS20190910(
new Config({
Expand All @@ -23,7 +24,7 @@ const client = new ROS20190910(
}),
);

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

const updateStack = async (stackId: string, templateBody: unknown, context: ActionContext) => {
const updateStack = async (stackId: string, templateBody: unknown, context: Context) => {
const parameters = context.parameters?.map(
(parameter) =>
new UpdateStackRequestParameters({
Expand Down Expand Up @@ -156,11 +157,8 @@ const getStackActionResult = async (
});
};

export const rosStackDeploy = async (
stackName: string,
templateBody: unknown,
context: ActionContext,
) => {
export const rosStackDeploy = async (stackName: string, templateBody: unknown) => {
const context = getContext();
const stackInfo = await getStackByName(stackName, context.region);
if (stackInfo) {
const { Status: stackStatus } = stackInfo;
Expand All @@ -184,8 +182,9 @@ export const rosStackDeploy = async (
export const rosStackDelete = async ({
stackName,
region,
}: Pick<ActionContext, 'stackName' | 'region' | 'provider'>) => {
}: Pick<Context, 'stackName' | 'region' | 'provider'>) => {
const stackInfo = await getStackByName(stackName, region);

if (!stackInfo) {
logger.warn(`Stack: ${stackName} not exists, skipped! 🚫`);
return;
Expand Down
41 changes: 15 additions & 26 deletions src/stack/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import * as ros from '@alicloud/ros-cdk-core';
import fs from 'node:fs';

import { ActionContext, ServerlessIac } from '../types';
import { ServerlessIac } from '../types';
import {
cleanupAssets,
constructAssets,
getContext,
logger,
ProviderEnum,
publishAssets,
Expand All @@ -14,11 +15,8 @@
import { RfsStack } from './rfsStack';
import { get } from 'lodash';

export const generateRosStackTemplate = (
stackName: string,
iac: ServerlessIac,
context: ActionContext,
) => {
export const generateRosStackTemplate = (stackName: string, iac: ServerlessIac) => {
const context = getContext();
const app = new ros.App();
new RosStack(app, iac, context);

Expand All @@ -36,39 +34,31 @@
return { template, assets };
};

export const generateRfsStackTemplate = (
stackName: string,
iac: ServerlessIac,
context: ActionContext,
) => {
const stack = new RfsStack(iac, context);
export const generateRfsStackTemplate = (stackName: string, iac: ServerlessIac) => {
const stack = new RfsStack(iac);

Check warning on line 38 in src/stack/deploy.ts

View check run for this annotation

Codecov / codecov/patch

src/stack/deploy.ts#L38

Added line #L38 was not covered by tests

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

return { template: hcl };
};

export const deployStack = async (
stackName: string,
iac: ServerlessIac,
context: ActionContext,
) => {
const { template, assets } = generateRosStackTemplate(stackName, iac, context);
await prepareBootstrapStack(context);
export const deployStack = async (stackName: string, iac: ServerlessIac) => {
const { template, assets } = generateRosStackTemplate(stackName, iac);
await prepareBootstrapStack();
logger.info(`Deploying stack, publishing assets...`);
const constructedAssets = await constructAssets(assets, context.region);
const constructedAssets = await constructAssets(assets);
try {
await publishAssets(constructedAssets, context);
await publishAssets(constructedAssets);
logger.info(`Assets published! 🎉`);
await rosStackDeploy(stackName, template, context);
await rosStackDeploy(stackName, template);
} catch (e) {
logger.error(`Failed to deploy stack: ${e}`);
throw e;
} finally {
try {
logger.info(`Cleaning up temporary Assets...`);
await cleanupAssets(constructedAssets, context);
await cleanupAssets(constructedAssets);
logger.info(`Assets cleaned up!♻️`);
} catch (e) {
logger.error(
Expand All @@ -81,12 +71,11 @@
export const generateStackTemplate = (
stackName: string,
iac: ServerlessIac,
context: ActionContext,
): { template: unknown } => {
if (iac.provider.name === ProviderEnum.ALIYUN) {
return generateRosStackTemplate(stackName, iac, context);
return generateRosStackTemplate(stackName, iac);
} else if (iac.provider.name === ProviderEnum.HUAWEI) {
return generateRfsStackTemplate(stackName, iac, context);
return generateRfsStackTemplate(stackName, iac);

Check warning on line 78 in src/stack/deploy.ts

View check run for this annotation

Codecov / codecov/patch

src/stack/deploy.ts#L78

Added line #L78 was not covered by tests
}
return { template: '' };
};
Loading