@geek-fun/serverlessinsight 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +1 -0
- package/README.md +108 -8
- package/README.zh-CN.md +52 -8
- package/dist/package.json +37 -35
- package/dist/src/commands/deploy.js +17 -7
- package/dist/src/commands/destroy.js +27 -4
- package/dist/src/commands/forceUnlock.js +61 -0
- package/dist/src/commands/index.js +86 -14
- package/dist/src/commands/local.js +10 -1
- package/dist/src/commands/plan.js +33 -0
- package/dist/src/commands/template.js +3 -1
- package/dist/src/commands/validate.js +2 -1
- package/dist/src/common/aliyunClient/apigwOperations.js +652 -0
- package/dist/src/common/aliyunClient/dnsOperations.js +90 -0
- package/dist/src/common/aliyunClient/ecsOperations.js +141 -0
- package/dist/src/common/aliyunClient/esOperations.js +219 -0
- package/dist/src/common/aliyunClient/fc3Operations.js +270 -0
- package/dist/src/common/aliyunClient/index.js +141 -0
- package/dist/src/common/aliyunClient/nasOperations.js +233 -0
- package/dist/src/common/aliyunClient/ossOperations.js +237 -0
- package/dist/src/common/aliyunClient/ramOperations.js +205 -0
- package/dist/src/common/aliyunClient/rdsOperations.js +206 -0
- package/dist/src/common/aliyunClient/slsOperations.js +218 -0
- package/dist/src/common/aliyunClient/tablestoreOperations.js +199 -0
- package/dist/src/common/aliyunClient/types.js +2 -0
- package/dist/src/common/constants.js +7 -1
- package/dist/src/common/context.js +32 -14
- package/dist/src/common/credentials.js +39 -0
- package/dist/src/common/dependencyGraph/graph.js +280 -0
- package/dist/src/common/dependencyGraph/index.js +18 -0
- package/dist/src/common/dependencyGraph/types.js +2 -0
- package/dist/src/common/fileUtils.js +16 -0
- package/dist/src/common/hashUtils.js +121 -0
- package/dist/src/common/iacHelper.js +25 -97
- package/dist/src/common/imsClient.js +4 -0
- package/dist/src/common/index.js +7 -2
- package/dist/src/common/lockManager.js +212 -0
- package/dist/src/common/logger.js +89 -6
- package/dist/src/common/providerEnum.js +2 -3
- package/dist/src/common/runtimeMapper.js +160 -0
- package/dist/src/common/scfClient.js +84 -0
- package/dist/src/common/stateManager.js +107 -0
- package/dist/src/common/tencentClient/cosOperations.js +287 -0
- package/dist/src/common/tencentClient/esOperations.js +156 -0
- package/dist/src/common/tencentClient/index.js +116 -0
- package/dist/src/common/tencentClient/scfOperations.js +141 -0
- package/dist/src/common/tencentClient/tdsqlcOperations.js +211 -0
- package/dist/src/common/tencentClient/types.js +17 -0
- package/dist/src/lang/en.js +254 -0
- package/dist/src/lang/index.js +28 -8
- package/dist/src/lang/zh-CN.js +229 -0
- package/dist/src/parser/bucketParser.js +25 -12
- package/dist/src/parser/databaseParser.js +14 -10
- package/dist/src/parser/functionParser.js +19 -6
- package/dist/src/parser/parseUtils.js +74 -0
- package/dist/src/parser/tableParser.js +19 -17
- package/dist/src/stack/aliyunStack/apigwExecutor.js +84 -0
- package/dist/src/stack/aliyunStack/apigwPlanner.js +118 -0
- package/dist/src/stack/aliyunStack/apigwResource.js +339 -0
- package/dist/src/stack/aliyunStack/apigwTypes.js +125 -0
- package/dist/src/stack/aliyunStack/databaseExecutor.js +112 -0
- package/dist/src/stack/aliyunStack/databasePlanner.js +128 -0
- package/dist/src/stack/aliyunStack/databaseResource.js +228 -0
- package/dist/src/stack/aliyunStack/deployer.js +133 -0
- package/dist/src/stack/aliyunStack/destroyer.js +114 -0
- package/dist/src/stack/aliyunStack/esServerlessTypes.js +141 -0
- package/dist/src/stack/aliyunStack/fc3Executor.js +91 -0
- package/dist/src/stack/aliyunStack/fc3Planner.js +77 -0
- package/dist/src/stack/aliyunStack/fc3Resource.js +511 -0
- package/dist/src/stack/aliyunStack/fc3Types.js +76 -0
- package/dist/src/stack/aliyunStack/index.js +40 -0
- package/dist/src/stack/aliyunStack/ossExecutor.js +91 -0
- package/dist/src/stack/aliyunStack/ossPlanner.js +76 -0
- package/dist/src/stack/aliyunStack/ossResource.js +196 -0
- package/dist/src/stack/aliyunStack/ossTypes.js +50 -0
- package/dist/src/stack/aliyunStack/planner.js +37 -0
- package/dist/src/stack/aliyunStack/rdsTypes.js +217 -0
- package/dist/src/stack/aliyunStack/tablestoreExecutor.js +92 -0
- package/dist/src/stack/aliyunStack/tablestorePlanner.js +94 -0
- package/dist/src/stack/aliyunStack/tablestoreResource.js +120 -0
- package/dist/src/stack/aliyunStack/tablestoreTypes.js +77 -0
- package/dist/src/stack/bucketTypes.js +17 -0
- package/dist/src/stack/deploy.js +24 -77
- package/dist/src/stack/localStack/bucket.js +11 -6
- package/dist/src/stack/localStack/event.js +10 -5
- package/dist/src/stack/localStack/function.js +13 -7
- package/dist/src/stack/localStack/functionRunner.js +1 -1
- package/dist/src/stack/localStack/localServer.js +7 -6
- package/dist/src/stack/scfStack/cosExecutor.js +91 -0
- package/dist/src/stack/scfStack/cosPlanner.js +76 -0
- package/dist/src/stack/scfStack/cosResource.js +126 -0
- package/dist/src/stack/scfStack/cosTypes.js +46 -0
- package/dist/src/stack/scfStack/deployer.js +91 -0
- package/dist/src/stack/scfStack/destroyer.js +88 -0
- package/dist/src/stack/scfStack/esServerlessExecutor.js +105 -0
- package/dist/src/stack/scfStack/esServerlessPlanner.js +86 -0
- package/dist/src/stack/scfStack/esServerlessResource.js +94 -0
- package/dist/src/stack/scfStack/esServerlessTypes.js +48 -0
- package/dist/src/stack/scfStack/index.js +35 -0
- package/dist/src/stack/scfStack/planner.js +91 -0
- package/dist/src/stack/scfStack/scfExecutor.js +91 -0
- package/dist/src/stack/scfStack/scfPlanner.js +78 -0
- package/dist/src/stack/scfStack/scfResource.js +216 -0
- package/dist/src/stack/scfStack/scfTypes.js +41 -0
- package/dist/src/stack/scfStack/tdsqlcExecutor.js +105 -0
- package/dist/src/stack/scfStack/tdsqlcPlanner.js +90 -0
- package/dist/src/stack/scfStack/tdsqlcResource.js +146 -0
- package/dist/src/stack/scfStack/tdsqlcTypes.js +59 -0
- package/dist/src/types/domains/lock.js +2 -0
- package/dist/src/types/domains/resolvable.js +2 -0
- package/dist/src/types/domains/state.js +19 -0
- package/dist/src/types/index.js +4 -0
- package/dist/src/validator/bucketSchema.js +4 -10
- package/dist/src/validator/databaseSchema.js +36 -36
- package/dist/src/validator/eventSchema.js +3 -2
- package/dist/src/validator/functionSchema.js +51 -46
- package/dist/src/validator/iacSchema.js +52 -3
- package/dist/src/validator/rootSchema.js +47 -1
- package/dist/src/validator/tableschema.js +9 -8
- package/dist/src/validator/templateRefSchema.js +23 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +37 -35
- package/samples/README_TENCENT_COS.md +486 -0
- package/samples/README_TENCENT_SCF.md +272 -0
- package/samples/aliyun-poc-api.yml +1 -1
- package/samples/aliyun-poc-bucket.yml +0 -1
- package/samples/aliyun-poc-domain.yml +0 -1
- package/samples/aliyun-poc-es.yml +14 -13
- package/samples/aliyun-poc-rds.yml +0 -2
- package/samples/aliyun-poc-table.yml +1 -3
- package/samples/tencent-poc-cos.yml +20 -0
- package/samples/tencent-poc-scf.yml +36 -0
- package/dist/src/commands/index.d.ts +0 -2
- package/dist/src/common/index.d.ts +0 -11
- package/dist/src/common/rosAssets.js +0 -178
- package/dist/src/common/rosClient.js +0 -198
- package/dist/src/index.d.ts +0 -1
- package/dist/src/lang/index.d.ts +0 -3
- package/dist/src/parser/index.d.ts +0 -3
- package/dist/src/stack/index.d.ts +0 -1
- package/dist/src/stack/localStack/index.d.ts +0 -5
- package/dist/src/stack/rfsStack/index.d.ts +0 -9
- package/dist/src/stack/rosStack/bootstrap.js +0 -187
- package/dist/src/stack/rosStack/bucket.js +0 -127
- package/dist/src/stack/rosStack/database.js +0 -313
- package/dist/src/stack/rosStack/event.js +0 -143
- package/dist/src/stack/rosStack/function.js +0 -259
- package/dist/src/stack/rosStack/index.d.ts +0 -7
- package/dist/src/stack/rosStack/index.js +0 -75
- package/dist/src/stack/rosStack/stage.js +0 -46
- package/dist/src/stack/rosStack/table.js +0 -95
- package/dist/src/stack/rosStack/tag.js +0 -11
- package/dist/src/stack/rosStack/vars.js +0 -49
- package/dist/src/types/index.d.ts +0 -55
- package/dist/src/types/localStack/index.d.ts +0 -81
- package/dist/src/validator/index.d.ts +0 -1
- package/layers/si-bootstrap-sdk/Dockerfile-aliyuncli +0 -12
- package/layers/si-bootstrap-sdk/README.md +0 -1
- package/layers/si-bootstrap-sdk/package-lock.json +0 -875
- package/layers/si-bootstrap-sdk/package.json +0 -33
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateApigwPlan = void 0;
|
|
4
|
+
const aliyunClient_1 = require("../../common/aliyunClient");
|
|
5
|
+
const apigwTypes_1 = require("./apigwTypes");
|
|
6
|
+
const stateManager_1 = require("../../common/stateManager");
|
|
7
|
+
const hashUtils_1 = require("../../common/hashUtils");
|
|
8
|
+
const planEventDeletion = (logicalId, definition) => ({
|
|
9
|
+
logicalId,
|
|
10
|
+
action: 'delete',
|
|
11
|
+
resourceType: 'ALIYUN_APIGW',
|
|
12
|
+
changes: { before: definition },
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Generate plan for API Gateway events
|
|
16
|
+
*/
|
|
17
|
+
const generateApigwPlan = async (context, state, events, serviceName) => {
|
|
18
|
+
// If no events, check if we need to delete existing ones
|
|
19
|
+
if (!events || events.length === 0) {
|
|
20
|
+
const allStates = (0, stateManager_1.getAllResources)(state);
|
|
21
|
+
const items = Object.entries(allStates)
|
|
22
|
+
.filter(([logicalId]) => logicalId.startsWith('events.'))
|
|
23
|
+
.map(([logicalId, resourceState]) => planEventDeletion(logicalId, resourceState.definition));
|
|
24
|
+
return { items };
|
|
25
|
+
}
|
|
26
|
+
const desiredLogicalIds = new Set(events.map((e) => `events.${e.key}`));
|
|
27
|
+
const eventItems = await Promise.all(events.map(async (event) => {
|
|
28
|
+
const logicalId = `events.${event.key}`;
|
|
29
|
+
const currentState = (0, stateManager_1.getResource)(state, logicalId);
|
|
30
|
+
const groupConfig = (0, apigwTypes_1.eventToApigwGroupConfig)(event, serviceName);
|
|
31
|
+
const groupDefinition = (0, apigwTypes_1.extractApigwGroupDefinition)(groupConfig);
|
|
32
|
+
const client = (0, aliyunClient_1.createAliyunClient)(context);
|
|
33
|
+
// Build desired definition including triggers
|
|
34
|
+
const desiredDefinition = {
|
|
35
|
+
...groupDefinition,
|
|
36
|
+
triggers: event.triggers.map((t) => ({
|
|
37
|
+
method: t.method,
|
|
38
|
+
path: t.path,
|
|
39
|
+
backend: t.backend,
|
|
40
|
+
})),
|
|
41
|
+
domain: event.domain
|
|
42
|
+
? {
|
|
43
|
+
domainName: event.domain.domain_name,
|
|
44
|
+
certificateName: event.domain.certificate_name,
|
|
45
|
+
}
|
|
46
|
+
: null,
|
|
47
|
+
};
|
|
48
|
+
if (!currentState) {
|
|
49
|
+
// No state exists, check if resource exists remotely
|
|
50
|
+
try {
|
|
51
|
+
const remoteGroup = await client.apigw.findApiGroupByName(groupConfig.groupName);
|
|
52
|
+
if (remoteGroup) {
|
|
53
|
+
// Resource exists remotely but not in state - this is drift
|
|
54
|
+
// We should update (import) it rather than try to create again
|
|
55
|
+
return {
|
|
56
|
+
logicalId,
|
|
57
|
+
action: 'update',
|
|
58
|
+
resourceType: 'ALIYUN_APIGW',
|
|
59
|
+
changes: { after: desiredDefinition },
|
|
60
|
+
drifted: true,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Ignore errors when checking remote
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
logicalId,
|
|
69
|
+
action: 'create',
|
|
70
|
+
resourceType: 'ALIYUN_APIGW',
|
|
71
|
+
changes: { after: desiredDefinition },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// State exists, check for changes
|
|
75
|
+
try {
|
|
76
|
+
const groupInstance = currentState.instances.find((i) => i.type === 'ALIYUN_APIGW_GROUP');
|
|
77
|
+
if (groupInstance) {
|
|
78
|
+
const remoteGroup = await client.apigw.getApiGroup(groupInstance.id);
|
|
79
|
+
if (!remoteGroup) {
|
|
80
|
+
// Resource in state but not remotely - needs recreate
|
|
81
|
+
return {
|
|
82
|
+
logicalId,
|
|
83
|
+
action: 'create',
|
|
84
|
+
resourceType: 'ALIYUN_APIGW',
|
|
85
|
+
changes: { before: currentState.definition, after: desiredDefinition },
|
|
86
|
+
drifted: true,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const currentDefinition = currentState.definition || {};
|
|
91
|
+
const definitionChanged = !(0, hashUtils_1.attributesEqual)(currentDefinition, desiredDefinition);
|
|
92
|
+
if (definitionChanged) {
|
|
93
|
+
return {
|
|
94
|
+
logicalId,
|
|
95
|
+
action: 'update',
|
|
96
|
+
resourceType: 'ALIYUN_APIGW',
|
|
97
|
+
changes: { before: currentDefinition, after: desiredDefinition },
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return { logicalId, action: 'noop', resourceType: 'ALIYUN_APIGW' };
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return {
|
|
104
|
+
logicalId,
|
|
105
|
+
action: 'create',
|
|
106
|
+
resourceType: 'ALIYUN_APIGW',
|
|
107
|
+
changes: { before: currentState.definition, after: desiredDefinition },
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}));
|
|
111
|
+
// Find resources in state that are no longer in config
|
|
112
|
+
const allStates = (0, stateManager_1.getAllResources)(state);
|
|
113
|
+
const deletionItems = Object.entries(allStates)
|
|
114
|
+
.filter(([logicalId]) => logicalId.startsWith('events.') && !desiredLogicalIds.has(logicalId))
|
|
115
|
+
.map(([logicalId, resourceState]) => planEventDeletion(logicalId, resourceState.definition));
|
|
116
|
+
return { items: [...eventItems, ...deletionItems] };
|
|
117
|
+
};
|
|
118
|
+
exports.generateApigwPlan = generateApigwPlan;
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deleteApigwResource = exports.updateApigwResource = exports.readApigwResourceByName = exports.readApigwResource = exports.createApigwResource = void 0;
|
|
4
|
+
const aliyunClient_1 = require("../../common/aliyunClient");
|
|
5
|
+
const apigwTypes_1 = require("./apigwTypes");
|
|
6
|
+
const stateManager_1 = require("../../common/stateManager");
|
|
7
|
+
const logger_1 = require("../../common/logger");
|
|
8
|
+
const lang_1 = require("../../lang");
|
|
9
|
+
const buildApigwGroupInstanceFromProvider = (info, region) => {
|
|
10
|
+
return {
|
|
11
|
+
type: 'ALIYUN_APIGW_GROUP',
|
|
12
|
+
arn: `arn:acs:apigateway:${region}:group/${info.groupId}`,
|
|
13
|
+
id: info.groupId ?? '',
|
|
14
|
+
groupId: info.groupId ?? null,
|
|
15
|
+
groupName: info.groupName ?? null,
|
|
16
|
+
description: info.description ?? null,
|
|
17
|
+
basePath: info.basePath ?? null,
|
|
18
|
+
subDomain: info.subDomain ?? null,
|
|
19
|
+
instanceId: info.instanceId ?? null,
|
|
20
|
+
instanceType: info.instanceType ?? null,
|
|
21
|
+
status: info.status ?? null,
|
|
22
|
+
createdTime: info.createdTime ?? null,
|
|
23
|
+
modifiedTime: info.modifiedTime ?? null,
|
|
24
|
+
billingStatus: info.billingStatus ?? null,
|
|
25
|
+
illegalStatus: info.illegalStatus ?? null,
|
|
26
|
+
trafficLimit: info.trafficLimit ?? null,
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
const buildApigwApiInstanceFromProvider = (info, region, groupId) => {
|
|
30
|
+
return {
|
|
31
|
+
type: 'ALIYUN_APIGW_API',
|
|
32
|
+
arn: `arn:acs:apigateway:${region}:group/${groupId}/api/${info.apiId}`,
|
|
33
|
+
id: info.apiId ?? '',
|
|
34
|
+
apiId: info.apiId ?? null,
|
|
35
|
+
apiName: info.apiName ?? null,
|
|
36
|
+
groupId: info.groupId ?? null,
|
|
37
|
+
groupName: info.groupName ?? null,
|
|
38
|
+
description: info.description ?? null,
|
|
39
|
+
visibility: info.visibility ?? null,
|
|
40
|
+
authType: info.authType ?? null,
|
|
41
|
+
requestConfig: info.requestConfig ?? null,
|
|
42
|
+
serviceConfig: info.serviceConfig ?? null,
|
|
43
|
+
resultType: info.resultType ?? null,
|
|
44
|
+
createdTime: info.createdTime ?? null,
|
|
45
|
+
modifiedTime: info.modifiedTime ?? null,
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
const buildApigwDeploymentInstance = (groupId, apiId, stageName, region) => {
|
|
49
|
+
return {
|
|
50
|
+
type: 'ALIYUN_APIGW_DEPLOYMENT',
|
|
51
|
+
arn: `arn:acs:apigateway:${region}:group/${groupId}/api/${apiId}/deployment/${stageName}`,
|
|
52
|
+
id: `${groupId}/${apiId}/${stageName}`,
|
|
53
|
+
groupId,
|
|
54
|
+
apiId,
|
|
55
|
+
stageName,
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
const createApigwResource = async (context, event, serviceName, roleArn, state) => {
|
|
59
|
+
const logicalId = `events.${event.key}`;
|
|
60
|
+
const client = (0, aliyunClient_1.createAliyunClient)(context);
|
|
61
|
+
const groupConfig = (0, apigwTypes_1.eventToApigwGroupConfig)(event, serviceName);
|
|
62
|
+
let groupId;
|
|
63
|
+
try {
|
|
64
|
+
const existingGroup = await client.apigw.findApiGroupByName(groupConfig.groupName);
|
|
65
|
+
if (existingGroup?.groupId) {
|
|
66
|
+
logger_1.logger.info(lang_1.lang.__('APIGW_GROUP_FOUND_REUSING', { groupName: groupConfig.groupName }));
|
|
67
|
+
groupId = existingGroup.groupId;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
groupId = await client.apigw.createApiGroup(groupConfig);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
logger_1.logger.debug(`Could not find existing group, creating new: ${error}`);
|
|
75
|
+
groupId = await client.apigw.createApiGroup(groupConfig);
|
|
76
|
+
}
|
|
77
|
+
// Get group info for state
|
|
78
|
+
const groupInfo = await client.apigw.getApiGroup(groupId);
|
|
79
|
+
if (!groupInfo) {
|
|
80
|
+
throw new Error(`Failed to get API group info after creation: ${groupId}`);
|
|
81
|
+
}
|
|
82
|
+
const instances = [
|
|
83
|
+
buildApigwGroupInstanceFromProvider(groupInfo, context.region),
|
|
84
|
+
];
|
|
85
|
+
const groupDefinition = (0, apigwTypes_1.extractApigwGroupDefinition)(groupConfig);
|
|
86
|
+
const partialResourceState = {
|
|
87
|
+
mode: 'managed',
|
|
88
|
+
region: context.region,
|
|
89
|
+
definition: {
|
|
90
|
+
...groupDefinition,
|
|
91
|
+
triggers: event.triggers.map((t) => ({
|
|
92
|
+
method: t.method,
|
|
93
|
+
path: t.path,
|
|
94
|
+
backend: t.backend,
|
|
95
|
+
})),
|
|
96
|
+
domain: event.domain
|
|
97
|
+
? {
|
|
98
|
+
domainName: event.domain.domain_name,
|
|
99
|
+
certificateName: event.domain.certificate_name,
|
|
100
|
+
}
|
|
101
|
+
: null,
|
|
102
|
+
},
|
|
103
|
+
instances,
|
|
104
|
+
lastUpdated: new Date().toISOString(),
|
|
105
|
+
};
|
|
106
|
+
state = (0, stateManager_1.setResource)(state, logicalId, partialResourceState);
|
|
107
|
+
// Create APIs and deployments for each trigger
|
|
108
|
+
for (const trigger of event.triggers) {
|
|
109
|
+
const apiConfig = (0, apigwTypes_1.triggerToApigwApiConfig)(event, trigger, groupId, serviceName, context.region, roleArn);
|
|
110
|
+
const apiId = await client.apigw.createApi(apiConfig);
|
|
111
|
+
// Get API info for state
|
|
112
|
+
const apiInfo = await client.apigw.getApi(groupId, apiId);
|
|
113
|
+
if (apiInfo) {
|
|
114
|
+
instances.push(buildApigwApiInstanceFromProvider(apiInfo, context.region, groupId));
|
|
115
|
+
}
|
|
116
|
+
// Deploy API to RELEASE stage
|
|
117
|
+
const deploymentConfig = {
|
|
118
|
+
groupId,
|
|
119
|
+
apiId,
|
|
120
|
+
stageName: 'RELEASE',
|
|
121
|
+
description: `${serviceName} API deployment for ${trigger.method} ${trigger.path}`,
|
|
122
|
+
};
|
|
123
|
+
await client.apigw.deployApi(deploymentConfig);
|
|
124
|
+
instances.push(buildApigwDeploymentInstance(groupId, apiId, 'RELEASE', context.region));
|
|
125
|
+
const updatedResourceState = {
|
|
126
|
+
mode: 'managed',
|
|
127
|
+
region: context.region,
|
|
128
|
+
definition: {
|
|
129
|
+
...groupDefinition,
|
|
130
|
+
triggers: event.triggers.map((t) => ({
|
|
131
|
+
method: t.method,
|
|
132
|
+
path: t.path,
|
|
133
|
+
backend: t.backend,
|
|
134
|
+
})),
|
|
135
|
+
domain: event.domain
|
|
136
|
+
? {
|
|
137
|
+
domainName: event.domain.domain_name,
|
|
138
|
+
certificateName: event.domain.certificate_name,
|
|
139
|
+
}
|
|
140
|
+
: null,
|
|
141
|
+
},
|
|
142
|
+
instances,
|
|
143
|
+
lastUpdated: new Date().toISOString(),
|
|
144
|
+
};
|
|
145
|
+
state = (0, stateManager_1.setResource)(state, logicalId, updatedResourceState);
|
|
146
|
+
}
|
|
147
|
+
if (event.domain) {
|
|
148
|
+
try {
|
|
149
|
+
state = await client.apigw.bindCustomDomain({
|
|
150
|
+
groupId,
|
|
151
|
+
domainName: event.domain.domain_name,
|
|
152
|
+
certificateName: event.domain.certificate_name,
|
|
153
|
+
certificateBody: event.domain.certificate_body,
|
|
154
|
+
certificatePrivateKey: event.domain.certificate_private_key,
|
|
155
|
+
}, state, logicalId);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
logger_1.logger.error(lang_1.lang.__('APIGW_DOMAIN_BINDING_FAILED', { error: String(error) }));
|
|
159
|
+
logger_1.logger.info(lang_1.lang.__('APIGW_GROUP_APIS_CREATED_DOMAIN_FAILED'));
|
|
160
|
+
logger_1.logger.info(lang_1.lang.__('APIGW_STATE_SAVED_RETRY'));
|
|
161
|
+
return state;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Update final state with all instances (group + APIs + deployments)
|
|
165
|
+
const finalResourceState = {
|
|
166
|
+
mode: 'managed',
|
|
167
|
+
region: context.region,
|
|
168
|
+
definition: {
|
|
169
|
+
...groupDefinition,
|
|
170
|
+
triggers: event.triggers.map((t) => ({
|
|
171
|
+
method: t.method,
|
|
172
|
+
path: t.path,
|
|
173
|
+
backend: t.backend,
|
|
174
|
+
})),
|
|
175
|
+
domain: event.domain
|
|
176
|
+
? {
|
|
177
|
+
domainName: event.domain.domain_name,
|
|
178
|
+
certificateName: event.domain.certificate_name,
|
|
179
|
+
}
|
|
180
|
+
: null,
|
|
181
|
+
},
|
|
182
|
+
instances,
|
|
183
|
+
lastUpdated: new Date().toISOString(),
|
|
184
|
+
};
|
|
185
|
+
return (0, stateManager_1.setResource)(state, logicalId, finalResourceState);
|
|
186
|
+
};
|
|
187
|
+
exports.createApigwResource = createApigwResource;
|
|
188
|
+
const readApigwResource = async (context, groupId) => {
|
|
189
|
+
const client = (0, aliyunClient_1.createAliyunClient)(context);
|
|
190
|
+
return await client.apigw.getApiGroup(groupId);
|
|
191
|
+
};
|
|
192
|
+
exports.readApigwResource = readApigwResource;
|
|
193
|
+
const readApigwResourceByName = async (context, groupName) => {
|
|
194
|
+
const client = (0, aliyunClient_1.createAliyunClient)(context);
|
|
195
|
+
return await client.apigw.findApiGroupByName(groupName);
|
|
196
|
+
};
|
|
197
|
+
exports.readApigwResourceByName = readApigwResourceByName;
|
|
198
|
+
const updateApigwResource = async (context, event, serviceName, roleArn, state) => {
|
|
199
|
+
const logicalId = `events.${event.key}`;
|
|
200
|
+
const existingState = (0, stateManager_1.getResource)(state, logicalId);
|
|
201
|
+
const client = (0, aliyunClient_1.createAliyunClient)(context);
|
|
202
|
+
if (!existingState) {
|
|
203
|
+
return (0, exports.createApigwResource)(context, event, serviceName, roleArn, state);
|
|
204
|
+
}
|
|
205
|
+
const existingInstances = existingState.instances;
|
|
206
|
+
const groupInstance = existingInstances.find((i) => i.type === 'ALIYUN_APIGW_GROUP');
|
|
207
|
+
if (!groupInstance) {
|
|
208
|
+
return (0, exports.createApigwResource)(context, event, serviceName, roleArn, state);
|
|
209
|
+
}
|
|
210
|
+
const groupId = groupInstance.id;
|
|
211
|
+
const groupConfig = (0, apigwTypes_1.eventToApigwGroupConfig)(event, serviceName);
|
|
212
|
+
await client.apigw.updateApiGroup(groupId, groupConfig);
|
|
213
|
+
const groupInfo = await client.apigw.getApiGroup(groupId);
|
|
214
|
+
if (!groupInfo) {
|
|
215
|
+
throw new Error(`Failed to get API group info after update: ${groupId}`);
|
|
216
|
+
}
|
|
217
|
+
const instances = [
|
|
218
|
+
buildApigwGroupInstanceFromProvider(groupInfo, context.region),
|
|
219
|
+
];
|
|
220
|
+
const existingApis = existingInstances.filter((i) => i.type === 'ALIYUN_APIGW_API');
|
|
221
|
+
const neededApiKeys = new Set();
|
|
222
|
+
for (const trigger of event.triggers) {
|
|
223
|
+
const apiConfig = (0, apigwTypes_1.triggerToApigwApiConfig)(event, trigger, groupId, serviceName, context.region, roleArn);
|
|
224
|
+
const apiKey = (0, apigwTypes_1.generateApiKey)(trigger.method, trigger.path);
|
|
225
|
+
neededApiKeys.add(apiKey);
|
|
226
|
+
const existingApi = existingApis.find((a) => {
|
|
227
|
+
return a.id && a.apiName === apiConfig.apiName;
|
|
228
|
+
});
|
|
229
|
+
let apiId;
|
|
230
|
+
if (existingApi) {
|
|
231
|
+
apiId = existingApi.id;
|
|
232
|
+
await client.apigw.updateApi(apiId, apiConfig);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
apiId = await client.apigw.createApi(apiConfig);
|
|
236
|
+
}
|
|
237
|
+
const apiInfo = await client.apigw.getApi(groupId, apiId);
|
|
238
|
+
if (apiInfo) {
|
|
239
|
+
instances.push(buildApigwApiInstanceFromProvider(apiInfo, context.region, groupId));
|
|
240
|
+
}
|
|
241
|
+
const deploymentConfig = {
|
|
242
|
+
groupId,
|
|
243
|
+
apiId,
|
|
244
|
+
stageName: 'RELEASE',
|
|
245
|
+
description: `${serviceName} API deployment for ${trigger.method} ${trigger.path}`,
|
|
246
|
+
};
|
|
247
|
+
await client.apigw.deployApi(deploymentConfig);
|
|
248
|
+
instances.push(buildApigwDeploymentInstance(groupId, apiId, 'RELEASE', context.region));
|
|
249
|
+
}
|
|
250
|
+
for (const existingApi of existingApis) {
|
|
251
|
+
const apiInfo = await client.apigw.getApi(groupId, existingApi.id);
|
|
252
|
+
if (apiInfo) {
|
|
253
|
+
const isNeeded = event.triggers.some((t) => {
|
|
254
|
+
const expectedName = (0, apigwTypes_1.triggerToApigwApiConfig)(event, t, groupId, serviceName, context.region, roleArn).apiName;
|
|
255
|
+
return apiInfo.apiName === expectedName;
|
|
256
|
+
});
|
|
257
|
+
if (!isNeeded) {
|
|
258
|
+
try {
|
|
259
|
+
await client.apigw.abolishApi(groupId, existingApi.id, 'RELEASE');
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
// API might not be deployed
|
|
263
|
+
}
|
|
264
|
+
await client.apigw.deleteApi(groupId, existingApi.id);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (event.domain) {
|
|
269
|
+
state = await client.apigw.bindCustomDomain({
|
|
270
|
+
groupId,
|
|
271
|
+
domainName: event.domain.domain_name,
|
|
272
|
+
certificateName: event.domain.certificate_name,
|
|
273
|
+
certificateBody: event.domain.certificate_body,
|
|
274
|
+
certificatePrivateKey: event.domain.certificate_private_key,
|
|
275
|
+
}, state, logicalId);
|
|
276
|
+
}
|
|
277
|
+
const groupDefinition = (0, apigwTypes_1.extractApigwGroupDefinition)(groupConfig);
|
|
278
|
+
const resourceState = {
|
|
279
|
+
mode: 'managed',
|
|
280
|
+
region: context.region,
|
|
281
|
+
definition: {
|
|
282
|
+
...groupDefinition,
|
|
283
|
+
triggers: event.triggers.map((t) => ({
|
|
284
|
+
method: t.method,
|
|
285
|
+
path: t.path,
|
|
286
|
+
backend: t.backend,
|
|
287
|
+
})),
|
|
288
|
+
domain: event.domain
|
|
289
|
+
? {
|
|
290
|
+
domainName: event.domain.domain_name,
|
|
291
|
+
certificateName: event.domain.certificate_name,
|
|
292
|
+
}
|
|
293
|
+
: null,
|
|
294
|
+
},
|
|
295
|
+
instances,
|
|
296
|
+
lastUpdated: new Date().toISOString(),
|
|
297
|
+
};
|
|
298
|
+
return (0, stateManager_1.setResource)(state, logicalId, resourceState);
|
|
299
|
+
};
|
|
300
|
+
exports.updateApigwResource = updateApigwResource;
|
|
301
|
+
const deleteApigwResource = async (context, logicalId, state) => {
|
|
302
|
+
const existingState = (0, stateManager_1.getResource)(state, logicalId);
|
|
303
|
+
const client = (0, aliyunClient_1.createAliyunClient)(context);
|
|
304
|
+
if (!existingState) {
|
|
305
|
+
return state;
|
|
306
|
+
}
|
|
307
|
+
const existingInstances = existingState.instances;
|
|
308
|
+
const groupInstance = existingInstances.find((i) => i.type === 'ALIYUN_APIGW_GROUP');
|
|
309
|
+
if (!groupInstance) {
|
|
310
|
+
return (0, stateManager_1.removeResource)(state, logicalId);
|
|
311
|
+
}
|
|
312
|
+
const groupId = groupInstance.id;
|
|
313
|
+
const deployments = existingInstances.filter((i) => i.type === 'ALIYUN_APIGW_DEPLOYMENT');
|
|
314
|
+
for (const deployment of deployments) {
|
|
315
|
+
try {
|
|
316
|
+
await client.apigw.abolishApi(deployment.groupId || groupId, deployment.apiId, deployment.stageName || 'RELEASE');
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
// Deployment might already be abolished
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
const apis = existingInstances.filter((i) => i.type === 'ALIYUN_APIGW_API');
|
|
323
|
+
for (const api of apis) {
|
|
324
|
+
try {
|
|
325
|
+
await client.apigw.deleteApi(groupId, api.id);
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
// API might already be deleted
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
try {
|
|
332
|
+
await client.apigw.deleteApiGroup(groupId);
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
// Group might already be deleted
|
|
336
|
+
}
|
|
337
|
+
return (0, stateManager_1.removeResource)(state, logicalId);
|
|
338
|
+
};
|
|
339
|
+
exports.deleteApigwResource = deleteApigwResource;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractApigwDeploymentDefinition = exports.extractApigwApiDefinition = exports.extractApigwGroupDefinition = exports.triggerToApigwApiConfig = exports.generateApiKey = exports.eventToApigwGroupConfig = void 0;
|
|
4
|
+
const common_1 = require("../../common");
|
|
5
|
+
/**
|
|
6
|
+
* Convert EventDomain to API Gateway group config
|
|
7
|
+
*/
|
|
8
|
+
const eventToApigwGroupConfig = (event, serviceName) => {
|
|
9
|
+
return {
|
|
10
|
+
groupName: `${serviceName}-agw-group`.replace(/_/g, '-'),
|
|
11
|
+
description: `API Gateway group for ${serviceName}`,
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
exports.eventToApigwGroupConfig = eventToApigwGroupConfig;
|
|
15
|
+
/**
|
|
16
|
+
* Generate a unique API key from method and path
|
|
17
|
+
* Uses URL encoding to preserve path structure and avoid collisions
|
|
18
|
+
*/
|
|
19
|
+
const generateApiKey = (method, path) => {
|
|
20
|
+
// Replace slashes with double underscores to preserve path structure
|
|
21
|
+
// Replace other non-alphanumeric chars with single underscore
|
|
22
|
+
const sanitizedPath = path
|
|
23
|
+
.replace(/\//g, '__')
|
|
24
|
+
.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
25
|
+
.replace(/^__/, '') // Remove leading double underscore
|
|
26
|
+
.replace(/__$/, ''); // Remove trailing double underscore
|
|
27
|
+
return `${method}_${sanitizedPath}`;
|
|
28
|
+
};
|
|
29
|
+
exports.generateApiKey = generateApiKey;
|
|
30
|
+
/**
|
|
31
|
+
* Resolves a function reference like ${functions.xxx} to the actual function name
|
|
32
|
+
*/
|
|
33
|
+
const resolveFunctionReference = (backendRef) => {
|
|
34
|
+
// Get IAC from context
|
|
35
|
+
const context = (0, common_1.getContext)();
|
|
36
|
+
if (!context.iac) {
|
|
37
|
+
common_1.logger.warn(`Cannot resolve function reference ${backendRef}: IAC not available in context`);
|
|
38
|
+
return backendRef;
|
|
39
|
+
}
|
|
40
|
+
// Use getIacDefinition to resolve function references
|
|
41
|
+
const functionDef = (0, common_1.getIacDefinition)(context.iac, backendRef);
|
|
42
|
+
if (!functionDef) {
|
|
43
|
+
// Not a function reference or function not found, return as is
|
|
44
|
+
common_1.logger.warn(`Function reference ${backendRef} could not be resolved from IAC`);
|
|
45
|
+
return backendRef;
|
|
46
|
+
}
|
|
47
|
+
const functionName = functionDef.name;
|
|
48
|
+
common_1.logger.info(`Resolved function reference ${backendRef} to function name: ${functionName}`);
|
|
49
|
+
return functionName;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Convert EventDomain trigger to API Gateway API config
|
|
53
|
+
*/
|
|
54
|
+
const triggerToApigwApiConfig = (event, trigger, groupId, serviceName, region, roleArn) => {
|
|
55
|
+
const method = trigger.method;
|
|
56
|
+
const path = trigger.path;
|
|
57
|
+
const backend = trigger.backend;
|
|
58
|
+
const resolvedFunctionName = resolveFunctionReference(backend);
|
|
59
|
+
const apiKey = (0, exports.generateApiKey)(method, path);
|
|
60
|
+
return {
|
|
61
|
+
groupId,
|
|
62
|
+
apiName: `${event.name}-agw-api-${apiKey}`.replace(/_/g, '-'),
|
|
63
|
+
visibility: 'PRIVATE',
|
|
64
|
+
authType: 'ANONYMOUS',
|
|
65
|
+
requestConfig: {
|
|
66
|
+
requestProtocol: 'HTTP',
|
|
67
|
+
requestHttpMethod: method,
|
|
68
|
+
requestPath: path,
|
|
69
|
+
requestMode: 'PASSTHROUGH',
|
|
70
|
+
},
|
|
71
|
+
serviceConfig: {
|
|
72
|
+
serviceProtocol: 'FunctionCompute',
|
|
73
|
+
functionComputeConfig: {
|
|
74
|
+
fcRegionId: region,
|
|
75
|
+
functionName: resolvedFunctionName,
|
|
76
|
+
roleArn,
|
|
77
|
+
fcVersion: '3.0',
|
|
78
|
+
method,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
resultType: 'PASSTHROUGH',
|
|
82
|
+
resultSample: 'ServerlessInsight resultSample',
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
exports.triggerToApigwApiConfig = triggerToApigwApiConfig;
|
|
86
|
+
/**
|
|
87
|
+
* Extract definition from API Gateway group config for state comparison
|
|
88
|
+
*/
|
|
89
|
+
const extractApigwGroupDefinition = (config) => {
|
|
90
|
+
return {
|
|
91
|
+
groupName: config.groupName,
|
|
92
|
+
description: config.description ?? null,
|
|
93
|
+
basePath: config.basePath ?? null,
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
exports.extractApigwGroupDefinition = extractApigwGroupDefinition;
|
|
97
|
+
/**
|
|
98
|
+
* Extract definition from API Gateway API config for state comparison
|
|
99
|
+
*/
|
|
100
|
+
const extractApigwApiDefinition = (config) => {
|
|
101
|
+
return {
|
|
102
|
+
apiName: config.apiName,
|
|
103
|
+
groupId: config.groupId,
|
|
104
|
+
visibility: config.visibility,
|
|
105
|
+
authType: config.authType,
|
|
106
|
+
requestConfig: config.requestConfig,
|
|
107
|
+
serviceConfig: {
|
|
108
|
+
serviceProtocol: config.serviceConfig.serviceProtocol,
|
|
109
|
+
functionComputeConfig: config.serviceConfig.functionComputeConfig ?? null,
|
|
110
|
+
},
|
|
111
|
+
resultType: config.resultType ?? null,
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
exports.extractApigwApiDefinition = extractApigwApiDefinition;
|
|
115
|
+
/**
|
|
116
|
+
* Extract definition from deployment config for state comparison
|
|
117
|
+
*/
|
|
118
|
+
const extractApigwDeploymentDefinition = (config) => {
|
|
119
|
+
return {
|
|
120
|
+
groupId: config.groupId,
|
|
121
|
+
apiId: config.apiId,
|
|
122
|
+
stageName: config.stageName,
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
exports.extractApigwDeploymentDefinition = extractApigwDeploymentDefinition;
|