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
10 changes: 9 additions & 1 deletion src/common/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@
location?: string;
parameters?: { [key: string]: string };
iacProvider?: ServerlessIac['provider'];
stages?: ServerlessIac['stages'];
}): void => {
const context = {
const context: Context = {
stage: config.stage ?? 'default',
stackName: config.stackName ?? '',
provider: (config.provider ?? config.iacProvider?.name ?? ProviderEnum.ALIYUN) as ProviderEnum,
Expand All @@ -42,6 +43,13 @@
securityToken: config.securityToken ?? process.env.ALIYUN_SECURITY_TOKEN,
iacLocation: getIacLocation(config.location),
parameters: Object.entries(config.parameters ?? {}).map(([key, value]) => ({ key, value })),
stages: Object.entries(config.stages ?? {}).reduce(
(acc, [stage, parameters]) => ({

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

View check run for this annotation

Codecov / codecov/patch

src/common/context.ts#L47

Added line #L47 was not covered by tests
...acc,
[stage]: Object.entries(parameters).map(([key, value]) => ({ key, value })),

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

View check run for this annotation

Codecov / codecov/patch

src/common/context.ts#L49

Added line #L49 was not covered by tests
}),
{},
),
};

asyncLocalStorage.enterWith(context);
Expand Down
84 changes: 52 additions & 32 deletions src/common/iacHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as ros from '@alicloud/ros-cdk-core';
import { Context } from '../types';
import * as ossDeployment from '@alicloud/ros-cdk-ossdeployment';
import crypto from 'node:crypto';
import { get } from 'lodash';

export const resolveCode = (location: string): string => {
const filePath = path.resolve(process.cwd(), location);
Expand Down Expand Up @@ -34,21 +35,17 @@ export const getFileSource = (
return { source, objectKey };
};

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: Context): T => {
if (typeof value === 'string') {
const matchVar = value.match(/^\$\{vars\.(\w+)}$/);
const containsVar = value.match(/\$\{vars\.(\w+)}/);
const matchMap = value.match(/^\$\{stages\.(\w+)}$/);
const containsMap = value.match(/\$\{stages\.(\w+)}/);
const matchFn = value.match(/^\$\{functions\.(\w+(\.\w+)?)}$/);
if (value.match(/\$\{ctx.\w+}/)) {
return evalCtx(value, ctx) as T;
export const calcRefs = <T>(rawValue: T, ctx: Context): T => {
if (typeof rawValue === 'string') {
const containsStage = rawValue.match(/\$\{ctx.\w+}/);
const matchVar = rawValue.match(/^\$\{vars\.(\w+)}$/);
const containsVar = rawValue.match(/\$\{vars\.(\w+)}/);
const matchMap = rawValue.match(/^\$\{stages\.(\w+)}$/);
const containsMap = rawValue.match(/\$\{stages\.(\w+)}/);
const matchFn = rawValue.match(/^\$\{functions\.(\w+(\.\w+)?)}$/);
let value = rawValue as string;
if (containsStage) {
value = value.replace(/\$\{ctx.stage}/g, ctx.stage);
}

if (matchVar?.length) {
Expand All @@ -57,33 +54,56 @@ export const replaceReference = <T>(value: T, ctx: Context): T => {
if (matchMap?.length) {
return ros.Fn.findInMap('stages', ctx.stage, matchMap[1]) as T;
}

if (matchFn?.length) {
return ros.Fn.getAtt(matchFn[1], 'FunctionName') as T;
}
if (containsMap?.length && containsVar?.length) {
return ros.Fn.sub(
value.replace(/\$\{stages\.(\w+)}/g, '${$1}').replace(/\$\{vars\.(\w+)}/g, '${$1}'),
) as T;
}
if (containsVar?.length) {
return ros.Fn.sub(value.replace(/\$\{vars\.(\w+)}/g, '${$1}')) as T;
}
if (containsMap?.length) {
return ros.Fn.sub(value.replace(/\$\{stages\.(\w+)}/g, '${$1}')) as T;

if (containsMap?.length || containsVar?.length) {
value = ros.Fn.sub(
rawValue.replace(/\$\{stages\.(\w+)}/g, '${$1}').replace(/\$\{vars\.(\w+)}/g, '${$1}'),
);
}
return value;

return value as T;
}

if (Array.isArray(value)) {
return value.map((item) => replaceReference(item, ctx)) as T;
if (Array.isArray(rawValue)) {
return rawValue.map((item) => calcRefs(item, ctx)) as T;
}

if (typeof value === 'object' && value !== null) {
if (typeof rawValue === 'object' && rawValue !== null) {
return Object.fromEntries(
Object.entries(value).map(([key, val]) => [key, replaceReference(val, ctx)]),
Object.entries(rawValue).map(([key, val]) => [key, calcRefs(val, ctx)]),
) as T;
}

return value;
return rawValue;
};

const getParam = (key: string, records?: Array<{ key: string; value: string }>) => {
return records?.find((param) => param.key === key)?.value as string;
};

export const realValue = <T>(rawValue: string, ctx: Context): T => {
const containsStage = rawValue.match(/\$\{ctx.stage}/);
const containsVar = rawValue.match(/\$\{vars.\w+}/);
const containsMap = rawValue.match(/\$\{stages\.(\w+)}/);

let value = rawValue;

if (containsStage?.length) {
value = rawValue.replace(/\$\{ctx.stage}/g, ctx.stage);
}

if (containsVar?.length) {
value = value.replace(/\$\{vars\.(\w+)}/g, (_, key) => getParam(key, ctx.parameters));
}

if (containsMap?.length) {
value = value.replace(/\$\{stages\.(\w+)}/g, (_, key) =>
getParam(key, get(ctx.stages, `${ctx.stage}`)),
);
}

return value as T;
};
18 changes: 9 additions & 9 deletions src/stack/rosStack/bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
encodeBase64ForRosId,
getAssets,
OSS_DEPLOYMENT_TIMEOUT,
replaceReference,
calcRefs,
splitDomain,
} from '../../common';
import * as ossDeployment from '@alicloud/ros-cdk-ossdeployment';
Expand Down Expand Up @@ -58,27 +58,27 @@ export const resolveBuckets = (
}

buckets.forEach((bucket) => {
const ossBucket = new oss.Bucket(scope, replaceReference(bucket.key, context), {
bucketName: replaceReference(bucket.name, context),
const ossBucket = new oss.Bucket(scope, calcRefs(bucket.key, context), {
bucketName: calcRefs(bucket.name, context),
accessControl: aclMap.get(
replaceReference(bucket.security?.acl, context) ?? ('' as BucketAccessEnum),
calcRefs(bucket.security?.acl, context) ?? ('' as BucketAccessEnum),
),
websiteConfigurationV2: bucket.website
? {
indexDocument: {
type: '0',
suffix: replaceReference(bucket.website.index, context),
suffix: calcRefs(bucket.website.index, context),
supportSubDir: 'true',
},
errorDocument: {
httpStatus: `${replaceReference(bucket.website.error_code, context)}`,
key: replaceReference(bucket.website.error_page, context),
httpStatus: `${calcRefs(bucket.website.error_code, context)}`,
key: calcRefs(bucket.website.error_page, context),
},
}
: undefined,
});
if (bucket.website?.code) {
const filePath = path.resolve(process.cwd(), replaceReference(bucket.website.code, context));
const filePath = path.resolve(process.cwd(), calcRefs(bucket.website.code, context));
new ossDeployment.BucketDeployment(
scope,
`si_auto_${bucket.key}_bucket_code_deployment`,
Expand All @@ -100,7 +100,7 @@ export const resolveBuckets = (
`${bucket.key}_custom_domain_${encodeBase64ForRosId(bucket.website.domain)}`,
{
bucketName: ossBucket.attrName,
domainName: replaceReference(bucket.website.domain, context),
domainName: calcRefs(bucket.website.domain, context),
},
);

Expand Down
22 changes: 11 additions & 11 deletions src/stack/rosStack/database.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as ros from '@alicloud/ros-cdk-core';
import * as rds from '@alicloud/ros-cdk-rds';
import { replaceReference } from '../../common';
import { calcRefs } from '../../common';
import { Context, DatabaseDomain, DatabaseEnum, DatabaseVersionEnum } from '../../types';
import { isEmpty } from 'lodash';
import * as esServerless from '@alicloud/ros-cdk-elasticsearchserverless';
Expand Down Expand Up @@ -208,14 +208,14 @@ export const resolveDatabases = (
if ([DatabaseEnum.ELASTICSEARCH_SERVERLESS].includes(db.type)) {
new esServerless.App(
scope,
replaceReference(db.key, context),
calcRefs(db.key, context),
{
appName: replaceReference(db.name, context),
appName: calcRefs(db.name, context),
appVersion: version,
authentication: {
basicAuth: [
{
password: replaceReference(db.security.basicAuth.password, context),
password: calcRefs(db.security.basicAuth.password, context),
},
],
},
Expand Down Expand Up @@ -249,7 +249,7 @@ export const resolveDatabases = (
) {
new rds.DBInstance(
scope,
replaceReference(db.key, context),
calcRefs(db.key, context),
{
engine: engine as string,
/**
Expand All @@ -259,7 +259,7 @@ export const resolveDatabases = (
* PostgreSQL:14.0、15.0、16.0 - PGSQL_HA_14, PGSQL_14 PGSQL_HA_15, PGSQL_15, PGSQL_HA_16,PGSQL_16
*/
engineVersion: version as string,
dbInstanceStorage: replaceReference(db.storage.min, context),
dbInstanceStorage: calcRefs(db.storage.min, context),
/** Serverless 实例
* serverless_basic:Serverless 基础系列。(仅适用 MySQL 和 PostgreSQL)
* serverless_standard:Serverless 高可用系列。(仅适用 MySQL 和 PostgreSQL)
Expand Down Expand Up @@ -297,11 +297,11 @@ export const resolveDatabases = (
*/
serverlessConfig: {
// @TODO db.cu.min should get parameter value when it refer to a parameter
minCapacity: replaceReference(
minCapacity: calcRefs(
db.cu.min === 0 ? quota!.minCapacity : db.cu.min + quota!.minCapacity,
context,
),
maxCapacity: replaceReference(
maxCapacity: calcRefs(
db.cu.max + quota!.minCapacity <= quota!.maxCapacity
? db.cu.max + quota!.minCapacity
: quota!.maxCapacity,
Expand All @@ -310,11 +310,11 @@ export const resolveDatabases = (
autoPause: db.cu.min === 0,
switchForce: false,
},
masterUsername: replaceReference(db.security.basicAuth.username, context),
masterUserPassword: replaceReference(db.security.basicAuth.password, context),
masterUsername: calcRefs(db.security.basicAuth.username, context),
masterUserPassword: calcRefs(db.security.basicAuth.password, context),
masterUserType: 'Super',
multiAz: quota!.ha,
securityIpList: replaceReference(db.network.ingressRules.join(','), context),
securityIpList: calcRefs(db.network.ingressRules.join(','), context),
connectionStringType: db.network.type === 'PRIVATE' ? 'Inner' : 'Public',
dbInstanceNetType: db.network.type === 'PRIVATE' ? 'Intranet' : 'Internet',
},
Expand Down
32 changes: 15 additions & 17 deletions src/stack/rosStack/event.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as ros from '@alicloud/ros-cdk-core';
import { Context, EventDomain, EventTypes, ServerlessIac } from '../../types';
import * as ram from '@alicloud/ros-cdk-ram';
import { encodeBase64ForRosId, replaceReference, splitDomain } from '../../common';
import { encodeBase64ForRosId, calcRefs, splitDomain } from '../../common';
import * as agw from '@alicloud/ros-cdk-apigateway';
import { isEmpty } from 'lodash';
import * as dns from '@alicloud/ros-cdk-dns';
Expand All @@ -22,10 +22,10 @@ export const resolveEvents = (
apiGateway.forEach((event) => {
const gatewayAccessRole = new ram.RosRole(
scope,
replaceReference(`${event.key}_role`, context),
calcRefs(`${event.key}_role`, context),
{
roleName: replaceReference(`${service}-${event.name}-agw-access-role`, context),
description: replaceReference(`${service} role`, context),
roleName: calcRefs(`${service}-${event.name}-agw-access-role`, context),
description: calcRefs(`${service} role`, context),
assumeRolePolicyDocument: {
version: '1',
statement: [
Expand All @@ -40,7 +40,7 @@ export const resolveEvents = (
},
policies: [
{
policyName: replaceReference(`${service}-${event.name}-policy`, context),
policyName: calcRefs(`${service}-${event.name}-policy`, context),
policyDocument: {
version: '1',
statement: [
Expand All @@ -60,10 +60,10 @@ export const resolveEvents = (

const apiGatewayGroup = new agw.RosGroup(
scope,
replaceReference(`${service}_apigroup`, context),
calcRefs(`${service}_apigroup`, context),
{
groupName: replaceReference(`${service}_apigroup`, context),
tags: replaceReference(tags, context),
groupName: calcRefs(`${service}_apigroup`, context),
tags: calcRefs(tags, context),
passthroughHeaders: 'host',
},
true,
Expand Down Expand Up @@ -96,37 +96,35 @@ export const resolveEvents = (
}

event.triggers.forEach((trigger) => {
const key = encodeBase64ForRosId(
replaceReference(`${trigger.method}_${trigger.path}`, context),
);
const key = encodeBase64ForRosId(calcRefs(`${trigger.method}_${trigger.path}`, context));

const api = new agw.RosApi(
scope,
`${event.key}_api_${key}`,
{
apiName: replaceReference(`${event.name}_api_${key}`, context),
apiName: calcRefs(`${event.name}_api_${key}`, context),
groupId: apiGatewayGroup.attrGroupId,
visibility: 'PRIVATE',
authType: 'ANONYMOUS',
requestConfig: {
requestProtocol: 'HTTP',
requestHttpMethod: replaceReference(trigger.method, context),
requestPath: replaceReference(trigger.path, context),
requestHttpMethod: calcRefs(trigger.method, context),
requestPath: calcRefs(trigger.path, context),
requestMode: 'PASSTHROUGH',
},
serviceConfig: {
serviceProtocol: 'FunctionCompute',
functionComputeConfig: {
fcRegionId: context.region,
functionName: replaceReference(trigger.backend, context),
functionName: calcRefs(trigger.backend, context),
roleArn: gatewayAccessRole.attrArn,
fcVersion: '3.0',
method: replaceReference(trigger.method, context),
method: calcRefs(trigger.method, context),
},
},
resultSample: 'ServerlessInsight resultSample',
resultType: 'PASSTHROUGH',
tags: replaceReference(tags, context),
tags: calcRefs(tags, context),
},
true,
);
Expand Down
Loading