@geek-fun/serverlessinsight 0.6.14 → 0.7.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/AGENTS.md +405 -10
- package/README.md +1 -0
- package/dist/package.json +8 -2
- package/dist/src/commands/deploy.js +4 -0
- package/dist/src/commands/destroy.js +4 -0
- package/dist/src/common/credentials.js +12 -0
- package/dist/src/common/providerEnum.js +1 -0
- package/dist/src/common/runtimeMapper.js +24 -3
- package/dist/src/common/volcengineClient/apigwOperations.js +271 -0
- package/dist/src/common/volcengineClient/iamOperations.js +349 -0
- package/dist/src/common/volcengineClient/index.js +99 -0
- package/dist/src/common/volcengineClient/tlsOperations.js +256 -0
- package/dist/src/common/volcengineClient/tosOperations.js +440 -0
- package/dist/src/common/volcengineClient/types.js +26 -0
- package/dist/src/common/volcengineClient/vefaasOperations.js +386 -0
- package/dist/src/lang/en.js +120 -0
- package/dist/src/lang/zh-CN.js +119 -0
- package/dist/src/stack/aliyunStack/fc3Resource.js +18 -3
- package/dist/src/stack/deploy.js +4 -0
- package/dist/src/stack/volcengineStack/apigwExecutor.js +87 -0
- package/dist/src/stack/volcengineStack/apigwPlanner.js +110 -0
- package/dist/src/stack/volcengineStack/apigwResource.js +302 -0
- package/dist/src/stack/volcengineStack/apigwTypes.js +106 -0
- package/dist/src/stack/volcengineStack/deployer.js +59 -0
- package/dist/src/stack/volcengineStack/destroyer.js +72 -0
- package/dist/src/stack/volcengineStack/index.js +44 -0
- package/dist/src/stack/volcengineStack/planner.js +27 -0
- package/dist/src/stack/volcengineStack/tosExecutor.js +106 -0
- package/dist/src/stack/volcengineStack/tosPlanner.js +96 -0
- package/dist/src/stack/volcengineStack/tosResource.js +103 -0
- package/dist/src/stack/volcengineStack/tosTypes.js +65 -0
- package/dist/src/stack/volcengineStack/vefaasExecutor.js +102 -0
- package/dist/src/stack/volcengineStack/vefaasPlanner.js +84 -0
- package/dist/src/stack/volcengineStack/vefaasResource.js +513 -0
- package/dist/src/stack/volcengineStack/vefaasTypes.js +75 -0
- package/dist/src/types/domains/state.js +3 -0
- package/dist/src/validator/functionSchema.js +13 -0
- package/dist/src/validator/rootSchema.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -2
- package/samples/volcengine-poc-advanced.yml +59 -0
- package/samples/volcengine-poc-api.yml +31 -0
- package/samples/volcengine-poc-bucket.yml +17 -0
- package/samples/volcengine-poc-function.yml +19 -0
- package/samples/volcengine-poc-vpc.yml +34 -0
|
@@ -579,9 +579,24 @@ const updateResource = async (context, fn, state) => {
|
|
|
579
579
|
};
|
|
580
580
|
}
|
|
581
581
|
const codePath = fn.code.path;
|
|
582
|
-
|
|
583
|
-
const
|
|
584
|
-
|
|
582
|
+
const currentCodeHash = existingState?.definition?.codeHash;
|
|
583
|
+
const desiredCodeHash = (0, common_1.computeFileHash)(codePath);
|
|
584
|
+
const codeChanged = currentCodeHash !== desiredCodeHash;
|
|
585
|
+
const existingConfig = existingState?.definition;
|
|
586
|
+
const desiredDefinition = (0, fc3Types_1.extractFc3Definition)(config, desiredCodeHash);
|
|
587
|
+
const { codeHash: _existingCodeHash, ...existingConfigOnly } = existingConfig || {};
|
|
588
|
+
const { codeHash: _desiredCodeHash, ...desiredConfigOnly } = desiredDefinition;
|
|
589
|
+
const configChanged = !(0, common_1.attributesEqual)(existingConfigOnly, desiredConfigOnly);
|
|
590
|
+
if (configChanged) {
|
|
591
|
+
await client.fc3.updateFunctionConfiguration(config);
|
|
592
|
+
}
|
|
593
|
+
if (codeChanged) {
|
|
594
|
+
const ossCode = await ensureOssCodeUpload(client, codePath, context.region, fn.name);
|
|
595
|
+
await client.fc3.updateFunctionCode(fn.name, codePath, ossCode);
|
|
596
|
+
}
|
|
597
|
+
if (!configChanged && !codeChanged) {
|
|
598
|
+
logger_1.logger.warn(lang_1.lang.__('UPDATING_RESOURCE_WITH_NO_CHANGES', { resourceType: 'function', name: fn.name }));
|
|
599
|
+
}
|
|
585
600
|
const functionInfo = await client.fc3.getFunction(fn.name);
|
|
586
601
|
if (!functionInfo) {
|
|
587
602
|
throw new Error(`Failed to refresh state for function: ${fn.name}`);
|
package/dist/src/stack/deploy.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.deployStack = void 0;
|
|
|
4
4
|
const common_1 = require("../common");
|
|
5
5
|
const scfStack_1 = require("./scfStack");
|
|
6
6
|
const aliyunStack_1 = require("./aliyunStack");
|
|
7
|
+
const volcengineStack_1 = require("./volcengineStack");
|
|
7
8
|
const deployHuawei = async () => {
|
|
8
9
|
throw new Error('Huawei deployment is not yet implemented. ' +
|
|
9
10
|
'The provider currently generates HCL templates but does not deploy them. ' +
|
|
@@ -19,5 +20,8 @@ const deployStack = async (iac, backend) => {
|
|
|
19
20
|
else if (iac.provider.name === common_1.ProviderEnum.HUAWEI) {
|
|
20
21
|
await deployHuawei();
|
|
21
22
|
}
|
|
23
|
+
else if (iac.provider.name === common_1.ProviderEnum.VOLCENGINE) {
|
|
24
|
+
await (0, volcengineStack_1.deployVolcengineStack)(iac, backend);
|
|
25
|
+
}
|
|
22
26
|
};
|
|
23
27
|
exports.deployStack = deployStack;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.executeApigwPlan = void 0;
|
|
4
|
+
const apigwResource_1 = require("./apigwResource");
|
|
5
|
+
const common_1 = require("../../common");
|
|
6
|
+
const stateManager_1 = require("../../common/stateManager");
|
|
7
|
+
const lang_1 = require("../../lang");
|
|
8
|
+
const executeSingleItem = async (context, item, eventsMap, serviceName, currentState) => {
|
|
9
|
+
switch (item.action) {
|
|
10
|
+
case 'noop':
|
|
11
|
+
common_1.logger.info(lang_1.lang.__('NO_CHANGESForResource', { logicalId: item.logicalId }));
|
|
12
|
+
return null;
|
|
13
|
+
case 'create': {
|
|
14
|
+
const event = eventsMap.get(item.logicalId);
|
|
15
|
+
if (!event) {
|
|
16
|
+
throw new Error(`Event not found for logical ID: ${item.logicalId}`);
|
|
17
|
+
}
|
|
18
|
+
common_1.logger.info(lang_1.lang.__('CREATING_RESOURCE', { resourceType: 'API Gateway resources', name: event.name }));
|
|
19
|
+
const newState = await (0, apigwResource_1.createApigwResource)(context, event, serviceName, currentState);
|
|
20
|
+
common_1.logger.info(lang_1.lang.__('RESOURCE_CREATED', { resourceType: 'API Gateway resources', name: event.name }));
|
|
21
|
+
return newState;
|
|
22
|
+
}
|
|
23
|
+
case 'update': {
|
|
24
|
+
const event = eventsMap.get(item.logicalId);
|
|
25
|
+
if (!event) {
|
|
26
|
+
throw new Error(`Event not found for logical ID: ${item.logicalId}`);
|
|
27
|
+
}
|
|
28
|
+
common_1.logger.info(lang_1.lang.__('UPDATING_RESOURCE', { resourceType: 'API Gateway resources', name: event.name }));
|
|
29
|
+
const newState = await (0, apigwResource_1.updateApigwResource)(context, event, serviceName, currentState);
|
|
30
|
+
common_1.logger.info(lang_1.lang.__('RESOURCE_UPDATED', { resourceType: 'API Gateway resources', name: event.name }));
|
|
31
|
+
return newState;
|
|
32
|
+
}
|
|
33
|
+
case 'delete': {
|
|
34
|
+
const state = (0, stateManager_1.getResource)(currentState, item.logicalId);
|
|
35
|
+
if (!state) {
|
|
36
|
+
common_1.logger.warn(lang_1.lang.__('STATE_NOT_FOUND_SKIPPING', { logicalId: item.logicalId }));
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
common_1.logger.info(lang_1.lang.__('DELETING_RESOURCE', {
|
|
40
|
+
resourceType: 'API Gateway resources',
|
|
41
|
+
name: item.logicalId,
|
|
42
|
+
}));
|
|
43
|
+
const newState = await (0, apigwResource_1.deleteApigwResource)(context, item.logicalId, currentState);
|
|
44
|
+
common_1.logger.info(lang_1.lang.__('RESOURCE_DELETED', {
|
|
45
|
+
resourceType: 'API Gateway resources',
|
|
46
|
+
name: item.logicalId,
|
|
47
|
+
}));
|
|
48
|
+
return newState;
|
|
49
|
+
}
|
|
50
|
+
default:
|
|
51
|
+
common_1.logger.warn(lang_1.lang.__('UNKNOWN_ACTION_FOR_RESOURCE', { action: item.action, logicalId: item.logicalId }));
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const executeApigwPlan = async (context, plan, events, serviceName, initialState, onStateChange) => {
|
|
56
|
+
const eventsMap = new Map(events?.map((e) => [`events.${e.key}`, e]) ?? []);
|
|
57
|
+
const successfulItems = [];
|
|
58
|
+
let currentState = initialState;
|
|
59
|
+
for (const item of plan.items) {
|
|
60
|
+
try {
|
|
61
|
+
const newState = await executeSingleItem(context, item, eventsMap, serviceName, currentState);
|
|
62
|
+
if (newState !== null) {
|
|
63
|
+
currentState = newState;
|
|
64
|
+
successfulItems.push(item);
|
|
65
|
+
if (onStateChange) {
|
|
66
|
+
onStateChange(currentState);
|
|
67
|
+
common_1.logger.debug(lang_1.lang.__('STATE_PERSISTED_AFTER_OPERATION', {
|
|
68
|
+
action: item.action,
|
|
69
|
+
resourceId: item.logicalId,
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
return {
|
|
76
|
+
state: currentState,
|
|
77
|
+
partialFailure: {
|
|
78
|
+
failedItem: item,
|
|
79
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
80
|
+
successfulItems,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { state: currentState };
|
|
86
|
+
};
|
|
87
|
+
exports.executeApigwPlan = executeApigwPlan;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateApigwPlan = void 0;
|
|
4
|
+
const volcengineClient_1 = require("../../common/volcengineClient");
|
|
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: 'VOLCENGINE_APIGW',
|
|
12
|
+
changes: { before: definition },
|
|
13
|
+
});
|
|
14
|
+
const generateApigwPlan = async (context, state, events, serviceName) => {
|
|
15
|
+
if (!events || events.length === 0) {
|
|
16
|
+
const allStates = (0, stateManager_1.getAllResources)(state);
|
|
17
|
+
const items = Object.entries(allStates)
|
|
18
|
+
.filter(([logicalId]) => logicalId.startsWith('events.'))
|
|
19
|
+
.map(([logicalId, resourceState]) => planEventDeletion(logicalId, resourceState.definition));
|
|
20
|
+
return { items };
|
|
21
|
+
}
|
|
22
|
+
const desiredLogicalIds = new Set(events.map((e) => `events.${e.key}`));
|
|
23
|
+
const eventItems = await Promise.all(events.map(async (event) => {
|
|
24
|
+
const logicalId = `events.${event.key}`;
|
|
25
|
+
const currentState = (0, stateManager_1.getResource)(state, logicalId);
|
|
26
|
+
const groupConfig = (0, apigwTypes_1.eventToApigwGroupConfig)(event, serviceName, context.stage);
|
|
27
|
+
const groupDefinition = (0, apigwTypes_1.extractApigwGroupDefinition)(groupConfig);
|
|
28
|
+
const client = (0, volcengineClient_1.createVolcengineClient)(context);
|
|
29
|
+
const desiredDefinition = {
|
|
30
|
+
...groupDefinition,
|
|
31
|
+
triggers: event.triggers.map((t) => ({
|
|
32
|
+
method: t.method,
|
|
33
|
+
path: t.path,
|
|
34
|
+
backend: t.backend,
|
|
35
|
+
})),
|
|
36
|
+
domain: (0, apigwTypes_1.extractEventDomainDefinition)(event.domain),
|
|
37
|
+
};
|
|
38
|
+
if (!currentState) {
|
|
39
|
+
try {
|
|
40
|
+
const remoteGateway = await client.apigw.findGatewayByName(groupConfig.groupName);
|
|
41
|
+
if (remoteGateway) {
|
|
42
|
+
return {
|
|
43
|
+
logicalId,
|
|
44
|
+
action: 'update',
|
|
45
|
+
resourceType: 'VOLCENGINE_APIGW',
|
|
46
|
+
changes: { after: desiredDefinition },
|
|
47
|
+
drifted: true,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Ignore errors when checking remote
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
logicalId,
|
|
56
|
+
action: 'create',
|
|
57
|
+
resourceType: 'VOLCENGINE_APIGW',
|
|
58
|
+
changes: { after: desiredDefinition },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const groupInstance = currentState.instances.find((i) => i.type === 'VOLCENGINE_APIGW_GROUP');
|
|
63
|
+
if (groupInstance) {
|
|
64
|
+
const remoteGateway = await client.apigw.getGateway(groupInstance.id);
|
|
65
|
+
if (!remoteGateway) {
|
|
66
|
+
return {
|
|
67
|
+
logicalId,
|
|
68
|
+
action: 'create',
|
|
69
|
+
resourceType: 'VOLCENGINE_APIGW',
|
|
70
|
+
changes: { before: currentState.definition, after: desiredDefinition },
|
|
71
|
+
drifted: true,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const currentDefinition = currentState.definition || {};
|
|
76
|
+
const definitionChanged = !(0, hashUtils_1.attributesEqual)(currentDefinition, desiredDefinition);
|
|
77
|
+
if (definitionChanged) {
|
|
78
|
+
return {
|
|
79
|
+
logicalId,
|
|
80
|
+
action: 'update',
|
|
81
|
+
resourceType: 'VOLCENGINE_APIGW',
|
|
82
|
+
changes: { before: currentDefinition, after: desiredDefinition },
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return { logicalId, action: 'noop', resourceType: 'VOLCENGINE_APIGW' };
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return {
|
|
89
|
+
logicalId,
|
|
90
|
+
action: 'create',
|
|
91
|
+
resourceType: 'VOLCENGINE_APIGW',
|
|
92
|
+
changes: { before: currentState.definition, after: desiredDefinition },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}));
|
|
96
|
+
const allStates = (0, stateManager_1.getAllResources)(state);
|
|
97
|
+
const deletionItems = Object.entries(allStates)
|
|
98
|
+
.filter(([logicalId]) => {
|
|
99
|
+
if (!logicalId.startsWith('events.')) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
if (desiredLogicalIds.has(logicalId)) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
})
|
|
107
|
+
.map(([logicalId, resourceState]) => planEventDeletion(logicalId, resourceState.definition));
|
|
108
|
+
return { items: [...eventItems, ...deletionItems] };
|
|
109
|
+
};
|
|
110
|
+
exports.generateApigwPlan = generateApigwPlan;
|
|
@@ -0,0 +1,302 @@
|
|
|
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 volcengineClient_1 = require("../../common/volcengineClient");
|
|
5
|
+
const apigwTypes_1 = require("./apigwTypes");
|
|
6
|
+
const stateManager_1 = require("../../common/stateManager");
|
|
7
|
+
const common_1 = require("../../common");
|
|
8
|
+
const logger_1 = require("../../common/logger");
|
|
9
|
+
const lang_1 = require("../../lang");
|
|
10
|
+
const buildApigwGroupInstanceFromProvider = (info, stage) => {
|
|
11
|
+
return {
|
|
12
|
+
type: 'VOLCENGINE_APIGW_GROUP',
|
|
13
|
+
sid: (0, common_1.buildSid)('volcengine', 'apigw', stage, info.gatewayId ?? ''),
|
|
14
|
+
id: info.gatewayId ?? '',
|
|
15
|
+
gatewayId: info.gatewayId ?? null,
|
|
16
|
+
gatewayName: info.gatewayName ?? null,
|
|
17
|
+
description: info.description ?? null,
|
|
18
|
+
protocol: info.protocol ?? null,
|
|
19
|
+
status: info.status ?? null,
|
|
20
|
+
createdTime: info.createdTime ?? null,
|
|
21
|
+
subDomain: info.subDomain ?? null,
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
const buildApigwApiInstanceFromProvider = (info, stage, gatewayId) => {
|
|
25
|
+
return {
|
|
26
|
+
type: 'VOLCENGINE_APIGW_API',
|
|
27
|
+
sid: (0, common_1.buildSid)('volcengine', 'apigw', stage, `${gatewayId}/${info.apiId}`),
|
|
28
|
+
id: info.apiId ?? '',
|
|
29
|
+
apiId: info.apiId ?? null,
|
|
30
|
+
apiName: info.apiName ?? null,
|
|
31
|
+
gatewayId: info.gatewayId ?? null,
|
|
32
|
+
method: info.method ?? null,
|
|
33
|
+
path: info.path ?? null,
|
|
34
|
+
description: info.description ?? null,
|
|
35
|
+
backendType: info.backendType ?? null,
|
|
36
|
+
backendFunctionName: info.backendFunctionName ?? null,
|
|
37
|
+
status: info.status ?? null,
|
|
38
|
+
createdTime: info.createdTime ?? null,
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
const buildApigwDeploymentInstance = (gatewayId, apiId, stageName, stage) => {
|
|
42
|
+
return {
|
|
43
|
+
type: 'VOLCENGINE_APIGW_DEPLOYMENT',
|
|
44
|
+
sid: (0, common_1.buildSid)('volcengine', 'apigw', stage, `${gatewayId}/${apiId}/${stageName}`),
|
|
45
|
+
id: `${gatewayId}/${apiId}/${stageName}`,
|
|
46
|
+
gatewayId,
|
|
47
|
+
apiId,
|
|
48
|
+
stageName,
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
const createApigwResource = async (context, event, serviceName, state) => {
|
|
52
|
+
const logicalId = `events.${event.key}`;
|
|
53
|
+
const client = (0, volcengineClient_1.createVolcengineClient)(context);
|
|
54
|
+
const groupConfig = (0, apigwTypes_1.eventToApigwGroupConfig)(event, serviceName, context.stage);
|
|
55
|
+
let gatewayId;
|
|
56
|
+
try {
|
|
57
|
+
const existingGateway = await client.apigw.findGatewayByName(groupConfig.groupName);
|
|
58
|
+
if (existingGateway?.gatewayId) {
|
|
59
|
+
logger_1.logger.info(lang_1.lang.__('APIGW_GROUP_FOUND_REUSING', { groupName: groupConfig.groupName }));
|
|
60
|
+
gatewayId = existingGateway.gatewayId;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const gatewayInfo = await client.apigw.createGateway(groupConfig);
|
|
64
|
+
gatewayId = gatewayInfo.gatewayId;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
logger_1.logger.debug(`Could not find existing gateway, creating new: ${error}`);
|
|
69
|
+
const gatewayInfo = await client.apigw.createGateway(groupConfig);
|
|
70
|
+
gatewayId = gatewayInfo.gatewayId;
|
|
71
|
+
}
|
|
72
|
+
const gatewayInfo = await client.apigw.getGateway(gatewayId);
|
|
73
|
+
if (!gatewayInfo) {
|
|
74
|
+
throw new Error(`Failed to get API Gateway info after creation: ${gatewayId}`);
|
|
75
|
+
}
|
|
76
|
+
const instances = [
|
|
77
|
+
buildApigwGroupInstanceFromProvider(gatewayInfo, context.stage),
|
|
78
|
+
];
|
|
79
|
+
const groupDefinition = (0, apigwTypes_1.extractApigwGroupDefinition)(groupConfig);
|
|
80
|
+
const partialResourceState = {
|
|
81
|
+
mode: 'managed',
|
|
82
|
+
region: context.region,
|
|
83
|
+
definition: {
|
|
84
|
+
...groupDefinition,
|
|
85
|
+
triggers: event.triggers.map((t) => ({
|
|
86
|
+
method: t.method,
|
|
87
|
+
path: t.path,
|
|
88
|
+
backend: t.backend,
|
|
89
|
+
})),
|
|
90
|
+
domain: (0, apigwTypes_1.extractEventDomainDefinition)(event.domain),
|
|
91
|
+
},
|
|
92
|
+
instances,
|
|
93
|
+
lastUpdated: new Date().toISOString(),
|
|
94
|
+
};
|
|
95
|
+
state = (0, stateManager_1.setResource)(state, logicalId, partialResourceState);
|
|
96
|
+
for (const trigger of event.triggers) {
|
|
97
|
+
const apiConfig = (0, apigwTypes_1.triggerToApigwApiConfig)(event, trigger, gatewayId, serviceName, context.region, context.stage);
|
|
98
|
+
const apiId = await client.apigw.createApi(apiConfig);
|
|
99
|
+
const apiInfo = await client.apigw.getApi(gatewayId, apiId);
|
|
100
|
+
if (apiInfo) {
|
|
101
|
+
instances.push(buildApigwApiInstanceFromProvider(apiInfo, context.stage, gatewayId));
|
|
102
|
+
}
|
|
103
|
+
await client.apigw.deployApi(gatewayId, apiId);
|
|
104
|
+
instances.push(buildApigwDeploymentInstance(gatewayId, apiId, 'RELEASE', context.stage));
|
|
105
|
+
const updatedResourceState = {
|
|
106
|
+
mode: 'managed',
|
|
107
|
+
region: context.region,
|
|
108
|
+
definition: {
|
|
109
|
+
...groupDefinition,
|
|
110
|
+
triggers: event.triggers.map((t) => ({
|
|
111
|
+
method: t.method,
|
|
112
|
+
path: t.path,
|
|
113
|
+
backend: t.backend,
|
|
114
|
+
})),
|
|
115
|
+
domain: (0, apigwTypes_1.extractEventDomainDefinition)(event.domain),
|
|
116
|
+
},
|
|
117
|
+
instances,
|
|
118
|
+
lastUpdated: new Date().toISOString(),
|
|
119
|
+
};
|
|
120
|
+
state = (0, stateManager_1.setResource)(state, logicalId, updatedResourceState);
|
|
121
|
+
}
|
|
122
|
+
if (event.domain) {
|
|
123
|
+
try {
|
|
124
|
+
const domainConfig = {
|
|
125
|
+
gatewayId,
|
|
126
|
+
domainName: event.domain.domain_name,
|
|
127
|
+
certificateId: event.domain.certificate_id,
|
|
128
|
+
};
|
|
129
|
+
await client.apigw.bindDomain(domainConfig);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
logger_1.logger.error(lang_1.lang.__('APIGW_DOMAIN_BINDING_FAILED', { error: String(error) }));
|
|
133
|
+
logger_1.logger.info(lang_1.lang.__('APIGW_GROUP_APIS_CREATED_DOMAIN_FAILED'));
|
|
134
|
+
logger_1.logger.info(lang_1.lang.__('APIGW_STATE_SAVED_RETRY'));
|
|
135
|
+
return state;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const finalResourceState = {
|
|
139
|
+
mode: 'managed',
|
|
140
|
+
region: context.region,
|
|
141
|
+
definition: {
|
|
142
|
+
...groupDefinition,
|
|
143
|
+
triggers: event.triggers.map((t) => ({
|
|
144
|
+
method: t.method,
|
|
145
|
+
path: t.path,
|
|
146
|
+
backend: t.backend,
|
|
147
|
+
})),
|
|
148
|
+
domain: (0, apigwTypes_1.extractEventDomainDefinition)(event.domain),
|
|
149
|
+
},
|
|
150
|
+
instances,
|
|
151
|
+
lastUpdated: new Date().toISOString(),
|
|
152
|
+
};
|
|
153
|
+
return (0, stateManager_1.setResource)(state, logicalId, finalResourceState);
|
|
154
|
+
};
|
|
155
|
+
exports.createApigwResource = createApigwResource;
|
|
156
|
+
const readApigwResource = async (context, gatewayId) => {
|
|
157
|
+
const client = (0, volcengineClient_1.createVolcengineClient)(context);
|
|
158
|
+
return await client.apigw.getGateway(gatewayId);
|
|
159
|
+
};
|
|
160
|
+
exports.readApigwResource = readApigwResource;
|
|
161
|
+
const readApigwResourceByName = async (context, gatewayName) => {
|
|
162
|
+
const client = (0, volcengineClient_1.createVolcengineClient)(context);
|
|
163
|
+
return await client.apigw.findGatewayByName(gatewayName);
|
|
164
|
+
};
|
|
165
|
+
exports.readApigwResourceByName = readApigwResourceByName;
|
|
166
|
+
const updateApigwResource = async (context, event, serviceName, state) => {
|
|
167
|
+
const logicalId = `events.${event.key}`;
|
|
168
|
+
const existingState = (0, stateManager_1.getResource)(state, logicalId);
|
|
169
|
+
const client = (0, volcengineClient_1.createVolcengineClient)(context);
|
|
170
|
+
if (!existingState) {
|
|
171
|
+
return (0, exports.createApigwResource)(context, event, serviceName, state);
|
|
172
|
+
}
|
|
173
|
+
const existingInstances = existingState.instances;
|
|
174
|
+
const groupInstance = existingInstances.find((i) => i.type === 'VOLCENGINE_APIGW_GROUP');
|
|
175
|
+
if (!groupInstance) {
|
|
176
|
+
return (0, exports.createApigwResource)(context, event, serviceName, state);
|
|
177
|
+
}
|
|
178
|
+
const gatewayId = groupInstance.id;
|
|
179
|
+
const groupConfig = (0, apigwTypes_1.eventToApigwGroupConfig)(event, serviceName, context.stage);
|
|
180
|
+
await client.apigw.updateGateway(gatewayId, groupConfig);
|
|
181
|
+
const gatewayInfo = await client.apigw.getGateway(gatewayId);
|
|
182
|
+
if (!gatewayInfo) {
|
|
183
|
+
throw new Error(`Failed to get API Gateway info after update: ${gatewayId}`);
|
|
184
|
+
}
|
|
185
|
+
const instances = [
|
|
186
|
+
buildApigwGroupInstanceFromProvider(gatewayInfo, context.stage),
|
|
187
|
+
];
|
|
188
|
+
const existingApis = existingInstances.filter((i) => i.type === 'VOLCENGINE_APIGW_API');
|
|
189
|
+
const neededApiKeys = new Set();
|
|
190
|
+
for (const trigger of event.triggers) {
|
|
191
|
+
const apiConfig = (0, apigwTypes_1.triggerToApigwApiConfig)(event, trigger, gatewayId, serviceName, context.region, context.stage);
|
|
192
|
+
const apiKey = (0, apigwTypes_1.generateApiKey)(trigger.method, trigger.path);
|
|
193
|
+
neededApiKeys.add(apiKey);
|
|
194
|
+
const existingApi = existingApis.find((a) => {
|
|
195
|
+
return a.id && a.apiName === apiConfig.apiName;
|
|
196
|
+
});
|
|
197
|
+
let apiId;
|
|
198
|
+
if (existingApi) {
|
|
199
|
+
apiId = existingApi.id;
|
|
200
|
+
await client.apigw.updateApi(apiId, apiConfig);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
apiId = await client.apigw.createApi(apiConfig);
|
|
204
|
+
}
|
|
205
|
+
const apiInfo = await client.apigw.getApi(gatewayId, apiId);
|
|
206
|
+
if (apiInfo) {
|
|
207
|
+
instances.push(buildApigwApiInstanceFromProvider(apiInfo, context.stage, gatewayId));
|
|
208
|
+
}
|
|
209
|
+
await client.apigw.deployApi(gatewayId, apiId);
|
|
210
|
+
instances.push(buildApigwDeploymentInstance(gatewayId, apiId, 'RELEASE', context.stage));
|
|
211
|
+
}
|
|
212
|
+
for (const existingApi of existingApis) {
|
|
213
|
+
const apiInfo = await client.apigw.getApi(gatewayId, existingApi.id);
|
|
214
|
+
if (apiInfo) {
|
|
215
|
+
const isNeeded = event.triggers.some((t) => {
|
|
216
|
+
const expectedName = (0, apigwTypes_1.triggerToApigwApiConfig)(event, t, gatewayId, serviceName, context.region, context.stage).apiName;
|
|
217
|
+
return apiInfo.apiName === expectedName;
|
|
218
|
+
});
|
|
219
|
+
if (!isNeeded) {
|
|
220
|
+
await client.apigw.deleteApi(gatewayId, existingApi.id);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (event.domain) {
|
|
225
|
+
const domainConfig = {
|
|
226
|
+
gatewayId,
|
|
227
|
+
domainName: event.domain.domain_name,
|
|
228
|
+
certificateId: event.domain.certificate_id,
|
|
229
|
+
};
|
|
230
|
+
await client.apigw.bindDomain(domainConfig);
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
const existingDomain = existingState.definition?.domain;
|
|
234
|
+
if (existingDomain?.domainName) {
|
|
235
|
+
const previousDomain = existingDomain.domainName;
|
|
236
|
+
try {
|
|
237
|
+
await client.apigw.unbindDomain(gatewayId, previousDomain);
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
logger_1.logger.warn(lang_1.lang.__('APIGW_DOMAIN_UNBIND_FAILED', { domain: previousDomain, error: String(error) }));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const groupDefinition = (0, apigwTypes_1.extractApigwGroupDefinition)(groupConfig);
|
|
245
|
+
const resourceState = {
|
|
246
|
+
mode: 'managed',
|
|
247
|
+
region: context.region,
|
|
248
|
+
definition: {
|
|
249
|
+
...groupDefinition,
|
|
250
|
+
triggers: event.triggers.map((t) => ({
|
|
251
|
+
method: t.method,
|
|
252
|
+
path: t.path,
|
|
253
|
+
backend: t.backend,
|
|
254
|
+
})),
|
|
255
|
+
domain: (0, apigwTypes_1.extractEventDomainDefinition)(event.domain),
|
|
256
|
+
},
|
|
257
|
+
instances,
|
|
258
|
+
lastUpdated: new Date().toISOString(),
|
|
259
|
+
};
|
|
260
|
+
return (0, stateManager_1.setResource)(state, logicalId, resourceState);
|
|
261
|
+
};
|
|
262
|
+
exports.updateApigwResource = updateApigwResource;
|
|
263
|
+
const deleteApigwResource = async (context, logicalId, state) => {
|
|
264
|
+
const existingState = (0, stateManager_1.getResource)(state, logicalId);
|
|
265
|
+
const client = (0, volcengineClient_1.createVolcengineClient)(context);
|
|
266
|
+
if (!existingState) {
|
|
267
|
+
return state;
|
|
268
|
+
}
|
|
269
|
+
const existingInstances = existingState.instances;
|
|
270
|
+
const groupInstance = existingInstances.find((i) => i.type === 'VOLCENGINE_APIGW_GROUP');
|
|
271
|
+
if (!groupInstance) {
|
|
272
|
+
return (0, stateManager_1.removeResource)(state, logicalId);
|
|
273
|
+
}
|
|
274
|
+
const gatewayId = groupInstance.id;
|
|
275
|
+
const existingDomain = existingState.definition?.domain;
|
|
276
|
+
if (existingDomain?.domainName) {
|
|
277
|
+
const primaryDomain = existingDomain.domainName;
|
|
278
|
+
try {
|
|
279
|
+
await client.apigw.unbindDomain(gatewayId, primaryDomain);
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
logger_1.logger.warn(lang_1.lang.__('APIGW_DOMAIN_UNBIND_FAILED', { domain: primaryDomain, error: String(error) }));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const apis = existingInstances.filter((i) => i.type === 'VOLCENGINE_APIGW_API');
|
|
286
|
+
for (const api of apis) {
|
|
287
|
+
try {
|
|
288
|
+
await client.apigw.deleteApi(gatewayId, api.id);
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
// API might already be deleted
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
try {
|
|
295
|
+
await client.apigw.deleteGateway(gatewayId);
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
// Gateway might already be deleted
|
|
299
|
+
}
|
|
300
|
+
return (0, stateManager_1.removeResource)(state, logicalId);
|
|
301
|
+
};
|
|
302
|
+
exports.deleteApigwResource = deleteApigwResource;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractEventDomainDefinition = exports.extractApigwApiDefinition = exports.extractApigwGroupDefinition = exports.triggerToApigwApiConfig = exports.generateApiKey = exports.eventToApigwGroupConfig = void 0;
|
|
4
|
+
const common_1 = require("../../common");
|
|
5
|
+
const lang_1 = require("../../lang");
|
|
6
|
+
/**
|
|
7
|
+
* Convert EventDomain to API Gateway group config
|
|
8
|
+
*/
|
|
9
|
+
const eventToApigwGroupConfig = (event, serviceName, stage) => {
|
|
10
|
+
return {
|
|
11
|
+
groupName: `${serviceName}-${stage}-apigw`.replace(/_/g, '-'),
|
|
12
|
+
description: `API Gateway for ${serviceName}`,
|
|
13
|
+
protocol: event.domain?.protocol ?? 'HTTP',
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
exports.eventToApigwGroupConfig = eventToApigwGroupConfig;
|
|
17
|
+
/**
|
|
18
|
+
* Generate a unique API key from method and path
|
|
19
|
+
* Uses URL encoding to preserve path structure and avoid collisions
|
|
20
|
+
*/
|
|
21
|
+
const generateApiKey = (method, path) => {
|
|
22
|
+
const sanitizedPath = path
|
|
23
|
+
.replace(/\//g, '__')
|
|
24
|
+
.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
25
|
+
.replace(/^__/, '')
|
|
26
|
+
.replace(/__$/, '');
|
|
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
|
+
const context = (0, common_1.getContext)();
|
|
35
|
+
if (!context.iac) {
|
|
36
|
+
common_1.logger.warn(lang_1.lang.__('CANNOT_RESOLVE_FUNCTION_REF', { backendRef }));
|
|
37
|
+
return backendRef;
|
|
38
|
+
}
|
|
39
|
+
const functionDef = (0, common_1.getIacDefinition)(context.iac, backendRef);
|
|
40
|
+
if (!functionDef || !(0, common_1.isFunctionDomain)(functionDef)) {
|
|
41
|
+
common_1.logger.warn(lang_1.lang.__('FUNCTION_REF_NOT_RESOLVED', { backendRef }));
|
|
42
|
+
return backendRef;
|
|
43
|
+
}
|
|
44
|
+
const functionName = functionDef.name;
|
|
45
|
+
common_1.logger.info(lang_1.lang.__('RESOLVED_FUNCTION_REF', { backendRef, functionName }));
|
|
46
|
+
return functionName;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Convert EventDomain trigger to API Gateway API config
|
|
50
|
+
*/
|
|
51
|
+
const triggerToApigwApiConfig = (event, trigger, gatewayId, _serviceName, _region, stage) => {
|
|
52
|
+
const method = trigger.method;
|
|
53
|
+
const path = trigger.path;
|
|
54
|
+
const backend = trigger.backend;
|
|
55
|
+
const resolvedFunctionName = resolveFunctionReference(backend);
|
|
56
|
+
const apiKey = (0, exports.generateApiKey)(method, path);
|
|
57
|
+
return {
|
|
58
|
+
gatewayId,
|
|
59
|
+
apiName: `${event.name}-${stage}-api-${apiKey}`.replace(/_/g, '-'),
|
|
60
|
+
method,
|
|
61
|
+
path,
|
|
62
|
+
backendFunctionName: resolvedFunctionName,
|
|
63
|
+
backendType: 'veFaaS',
|
|
64
|
+
requestTimeout: 60,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
exports.triggerToApigwApiConfig = triggerToApigwApiConfig;
|
|
68
|
+
/**
|
|
69
|
+
* Extract definition from API Gateway group config for state comparison
|
|
70
|
+
*/
|
|
71
|
+
const extractApigwGroupDefinition = (config) => {
|
|
72
|
+
return {
|
|
73
|
+
groupName: config.groupName,
|
|
74
|
+
description: config.description ?? null,
|
|
75
|
+
protocol: config.protocol ?? null,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
exports.extractApigwGroupDefinition = extractApigwGroupDefinition;
|
|
79
|
+
/**
|
|
80
|
+
* Extract definition from API Gateway API config for state comparison
|
|
81
|
+
*/
|
|
82
|
+
const extractApigwApiDefinition = (config) => {
|
|
83
|
+
return {
|
|
84
|
+
apiName: config.apiName,
|
|
85
|
+
gatewayId: config.gatewayId,
|
|
86
|
+
method: config.method,
|
|
87
|
+
path: config.path,
|
|
88
|
+
backendFunctionName: config.backendFunctionName,
|
|
89
|
+
backendType: config.backendType,
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
exports.extractApigwApiDefinition = extractApigwApiDefinition;
|
|
93
|
+
const extractEventDomainDefinition = (domain) => {
|
|
94
|
+
if (!domain) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
domainName: domain.domain_name,
|
|
99
|
+
wwwBindApex: domain.www_bind_apex === true,
|
|
100
|
+
certificateId: domain.certificate_id ?? null,
|
|
101
|
+
certificateBody: domain.certificate_body ?? null,
|
|
102
|
+
certificatePrivateKey: domain.certificate_private_key ? '(managed)' : null,
|
|
103
|
+
protocol: domain.protocol ?? null,
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
exports.extractEventDomainDefinition = extractEventDomainDefinition;
|