@geek-fun/serverlessinsight 0.6.15 → 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.
Files changed (44) hide show
  1. package/AGENTS.md +405 -10
  2. package/README.md +1 -0
  3. package/dist/package.json +8 -2
  4. package/dist/src/commands/deploy.js +4 -0
  5. package/dist/src/commands/destroy.js +4 -0
  6. package/dist/src/common/credentials.js +12 -0
  7. package/dist/src/common/providerEnum.js +1 -0
  8. package/dist/src/common/runtimeMapper.js +24 -3
  9. package/dist/src/common/volcengineClient/apigwOperations.js +271 -0
  10. package/dist/src/common/volcengineClient/iamOperations.js +349 -0
  11. package/dist/src/common/volcengineClient/index.js +99 -0
  12. package/dist/src/common/volcengineClient/tlsOperations.js +256 -0
  13. package/dist/src/common/volcengineClient/tosOperations.js +440 -0
  14. package/dist/src/common/volcengineClient/types.js +26 -0
  15. package/dist/src/common/volcengineClient/vefaasOperations.js +386 -0
  16. package/dist/src/lang/en.js +120 -0
  17. package/dist/src/lang/zh-CN.js +119 -0
  18. package/dist/src/stack/deploy.js +4 -0
  19. package/dist/src/stack/volcengineStack/apigwExecutor.js +87 -0
  20. package/dist/src/stack/volcengineStack/apigwPlanner.js +110 -0
  21. package/dist/src/stack/volcengineStack/apigwResource.js +302 -0
  22. package/dist/src/stack/volcengineStack/apigwTypes.js +106 -0
  23. package/dist/src/stack/volcengineStack/deployer.js +59 -0
  24. package/dist/src/stack/volcengineStack/destroyer.js +72 -0
  25. package/dist/src/stack/volcengineStack/index.js +44 -0
  26. package/dist/src/stack/volcengineStack/planner.js +27 -0
  27. package/dist/src/stack/volcengineStack/tosExecutor.js +106 -0
  28. package/dist/src/stack/volcengineStack/tosPlanner.js +96 -0
  29. package/dist/src/stack/volcengineStack/tosResource.js +103 -0
  30. package/dist/src/stack/volcengineStack/tosTypes.js +65 -0
  31. package/dist/src/stack/volcengineStack/vefaasExecutor.js +102 -0
  32. package/dist/src/stack/volcengineStack/vefaasPlanner.js +84 -0
  33. package/dist/src/stack/volcengineStack/vefaasResource.js +513 -0
  34. package/dist/src/stack/volcengineStack/vefaasTypes.js +75 -0
  35. package/dist/src/types/domains/state.js +3 -0
  36. package/dist/src/validator/functionSchema.js +13 -0
  37. package/dist/src/validator/rootSchema.js +1 -1
  38. package/dist/tsconfig.tsbuildinfo +1 -1
  39. package/package.json +8 -2
  40. package/samples/volcengine-poc-advanced.yml +59 -0
  41. package/samples/volcengine-poc-api.yml +31 -0
  42. package/samples/volcengine-poc-bucket.yml +17 -0
  43. package/samples/volcengine-poc-function.yml +19 -0
  44. package/samples/volcengine-poc-vpc.yml +34 -0
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateFunctionPlan = void 0;
4
+ const common_1 = require("../../common");
5
+ const stateManager_1 = require("../../common/stateManager");
6
+ const volcengineClient_1 = require("../../common/volcengineClient");
7
+ const vefaasTypes_1 = require("./vefaasTypes");
8
+ const planFunctionDeletion = (logicalId, definition) => ({
9
+ logicalId,
10
+ action: 'delete',
11
+ resourceType: 'VOLCENGINE_VEFAAS',
12
+ changes: { before: definition },
13
+ });
14
+ const generateFunctionPlan = async (context, state, functions) => {
15
+ if (!functions || functions.length === 0) {
16
+ const allStates = (0, stateManager_1.getAllResources)(state);
17
+ const items = Object.entries(allStates)
18
+ .filter(([logicalId]) => logicalId.startsWith('functions.'))
19
+ .map(([logicalId, resourceState]) => planFunctionDeletion(logicalId, resourceState.definition));
20
+ return { items };
21
+ }
22
+ const desiredLogicalIds = new Set(functions.map((fn) => `functions.${fn.key}`));
23
+ const functionItems = await Promise.all(functions.map(async (fn) => {
24
+ const logicalId = `functions.${fn.key}`;
25
+ const currentState = (0, common_1.getResource)(state, logicalId);
26
+ const config = (0, vefaasTypes_1.functionToVefaasConfig)(fn);
27
+ const codePath = fn.code.path;
28
+ const desiredCodeHash = (0, common_1.computeFileHash)(codePath);
29
+ const desiredDefinition = (0, vefaasTypes_1.extractVefaasDefinition)(config, desiredCodeHash);
30
+ if (!currentState || currentState.status === 'tainted') {
31
+ return {
32
+ logicalId,
33
+ action: 'create',
34
+ resourceType: 'VOLCENGINE_VEFAAS',
35
+ changes: { after: desiredDefinition },
36
+ };
37
+ }
38
+ try {
39
+ const client = (0, volcengineClient_1.createVolcengineClient)(context);
40
+ const remoteFunction = await client.vefaas.getFunction(fn.name);
41
+ if (!remoteFunction) {
42
+ return {
43
+ logicalId,
44
+ action: 'create',
45
+ resourceType: 'VOLCENGINE_VEFAAS',
46
+ changes: {
47
+ before: currentState.definition,
48
+ after: desiredDefinition,
49
+ },
50
+ drifted: true,
51
+ };
52
+ }
53
+ const currentDefinition = currentState.definition || {};
54
+ const definitionChanged = !(0, common_1.attributesEqual)(currentDefinition, desiredDefinition);
55
+ if (definitionChanged) {
56
+ return {
57
+ logicalId,
58
+ action: 'update',
59
+ resourceType: 'VOLCENGINE_VEFAAS',
60
+ changes: { before: currentDefinition, after: desiredDefinition },
61
+ drifted: true,
62
+ };
63
+ }
64
+ return { logicalId, action: 'noop', resourceType: 'VOLCENGINE_VEFAAS' };
65
+ }
66
+ catch {
67
+ return {
68
+ logicalId,
69
+ action: 'create',
70
+ resourceType: 'VOLCENGINE_VEFAAS',
71
+ changes: {
72
+ before: currentState.definition,
73
+ after: desiredDefinition,
74
+ },
75
+ };
76
+ }
77
+ }));
78
+ const allStates = (0, stateManager_1.getAllResources)(state);
79
+ const deletionItems = Object.entries(allStates)
80
+ .filter(([logicalId]) => logicalId.startsWith('functions.') && !desiredLogicalIds.has(logicalId))
81
+ .map(([logicalId, resourceState]) => planFunctionDeletion(logicalId, resourceState.definition));
82
+ return { items: [...functionItems, ...deletionItems] };
83
+ };
84
+ exports.generateFunctionPlan = generateFunctionPlan;
@@ -0,0 +1,513 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.deleteResource = exports.updateResource = exports.readResource = exports.createResource = void 0;
37
+ const volcengineClient_1 = require("../../common/volcengineClient");
38
+ const common_1 = require("../../common");
39
+ const types_1 = require("../../types");
40
+ const vefaasTypes_1 = require("./vefaasTypes");
41
+ const logger_1 = require("../../common/logger");
42
+ const lang_1 = require("../../lang");
43
+ const RECOVERY_GET_FUNCTION_DELAY_MS = 1500;
44
+ const IAM_ROLE_PROPAGATION_DELAY_MS = 3000;
45
+ const delay = async (ms) => {
46
+ await new Promise((resolve) => {
47
+ setTimeout(resolve, ms);
48
+ });
49
+ };
50
+ const isRecoverableCreateError = (error) => {
51
+ const code = error && typeof error === 'object' && 'code' in error && typeof error.code === 'string'
52
+ ? error.code.toLowerCase()
53
+ : '';
54
+ const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
55
+ return (code === 'readtimeout' ||
56
+ code === 'timeout' ||
57
+ code === 'requesttimeout' ||
58
+ code === 'econnreset' ||
59
+ code === 'etimedout' ||
60
+ message.includes('readtimeout') ||
61
+ message.includes('timeout') ||
62
+ message.includes('socket hang up') ||
63
+ message.includes('econnreset') ||
64
+ message.includes('etimedout'));
65
+ };
66
+ const buildVefaasInstanceFromProvider = (info, sid) => {
67
+ return {
68
+ type: 'VOLCENGINE_VEFAAS_FUNCTION',
69
+ sid,
70
+ id: info.functionName ?? '',
71
+ functionName: info.functionName ?? null,
72
+ functionId: info.functionId ?? null,
73
+ runtime: info.runtime ?? null,
74
+ handler: info.handler ?? null,
75
+ memorySize: info.memoryMb ?? null,
76
+ timeout: info.requestTimeout ?? null,
77
+ environment: info.environmentVariables ?? {},
78
+ description: info.description ?? null,
79
+ status: info.status ?? null,
80
+ createdTime: info.createdTime ?? null,
81
+ lastUpdateTime: info.lastModifiedTime ?? null,
82
+ role: info.role ?? null,
83
+ vpcConfig: info.vpcConfig
84
+ ? {
85
+ vpcId: info.vpcConfig.vpcId ?? null,
86
+ subnetIds: info.vpcConfig.subnetIds ?? [],
87
+ securityGroupIds: info.vpcConfig.securityGroupIds ?? [],
88
+ }
89
+ : {},
90
+ logConfig: info.logConfig
91
+ ? {
92
+ project: info.logConfig.project ?? null,
93
+ topic: info.logConfig.topic ?? null,
94
+ }
95
+ : null,
96
+ };
97
+ };
98
+ const createDependentResources = async (context, fn, serviceName, existingInstances = []) => {
99
+ const client = (0, volcengineClient_1.createVolcengineClient)(context);
100
+ const instances = [];
101
+ let logConfig;
102
+ const hasTlsProject = existingInstances.some((i) => i.type === 'VOLCENGINE_TLS_PROJECT');
103
+ const hasIamRole = existingInstances.some((i) => i.type === 'VOLCENGINE_IAM_ROLE');
104
+ if (fn.log) {
105
+ if (hasTlsProject) {
106
+ const tlsProjectInstance = existingInstances.find((i) => i.type === 'VOLCENGINE_TLS_PROJECT');
107
+ const tlsTopicInstance = existingInstances.find((i) => i.type === 'VOLCENGINE_TLS_TOPIC');
108
+ if (tlsProjectInstance && tlsTopicInstance) {
109
+ const [projectName, topicName] = tlsTopicInstance.id.split('/');
110
+ logConfig = { project: projectName, topic: topicName };
111
+ instances.push(...existingInstances.filter((i) => i.type.startsWith('VOLCENGINE_TLS_')));
112
+ }
113
+ }
114
+ else {
115
+ const projectName = `${serviceName}-${context.stage}-tls`;
116
+ const topicName = `${serviceName}-${context.stage}-logs`;
117
+ logger_1.logger.info(lang_1.lang.__('CREATING_TLS_PROJECT', { projectName }));
118
+ const project = await client.tls.createProject({
119
+ projectName,
120
+ description: `veFaaS logs for ${serviceName}`,
121
+ region: context.region,
122
+ });
123
+ instances.push({
124
+ type: 'VOLCENGINE_TLS_PROJECT',
125
+ id: projectName,
126
+ attributes: { ...project },
127
+ });
128
+ logger_1.logger.info(lang_1.lang.__('CREATING_TLS_TOPIC', { topicName }));
129
+ const topic = await client.tls.createTopic({
130
+ projectName,
131
+ topicName,
132
+ description: `Function logs for ${serviceName}`,
133
+ ttl: 30,
134
+ });
135
+ instances.push({
136
+ type: 'VOLCENGINE_TLS_TOPIC',
137
+ id: `${projectName}/${topicName}`,
138
+ attributes: { ...topic },
139
+ });
140
+ logger_1.logger.info(lang_1.lang.__('CREATING_TLS_INDEX', { topicName }));
141
+ await client.tls.createIndex({
142
+ projectName,
143
+ topicName,
144
+ fullTextIndex: {
145
+ delimiter: ' ,.?;!\n\t',
146
+ caseSensitive: false,
147
+ },
148
+ });
149
+ instances.push({
150
+ type: 'VOLCENGINE_TLS_INDEX',
151
+ id: `${projectName}/${topicName}/index`,
152
+ attributes: {},
153
+ });
154
+ logger_1.logger.info(lang_1.lang.__('WAITING_FOR_TLS_RESOURCES', { projectName, topicName }));
155
+ await client.tls.waitForProject(projectName);
156
+ await client.tls.waitForTopic(projectName, topicName);
157
+ logConfig = { project: projectName, topic: topicName };
158
+ }
159
+ }
160
+ const roleName = `${serviceName}-${context.stage}-role`;
161
+ const trustedServices = (0, vefaasTypes_1.getTrustedServicesForFunction)(fn, context);
162
+ if (hasIamRole) {
163
+ const iamRoleInstance = existingInstances.find((i) => i.type === 'VOLCENGINE_IAM_ROLE');
164
+ if (iamRoleInstance) {
165
+ instances.push(iamRoleInstance);
166
+ await client.iam.updateRoleTrustPolicy(iamRoleInstance.id, (0, vefaasTypes_1.buildDefaultTrustPolicy)(trustedServices));
167
+ }
168
+ }
169
+ else {
170
+ logger_1.logger.info(lang_1.lang.__('CREATING_IAM_ROLE', { roleName }));
171
+ const iamRole = await client.iam.createRole({
172
+ roleName,
173
+ displayName: roleName,
174
+ description: `veFaaS execution role for ${serviceName}`,
175
+ trustPolicy: (0, vefaasTypes_1.buildDefaultTrustPolicy)(trustedServices),
176
+ });
177
+ instances.push({
178
+ type: 'VOLCENGINE_IAM_ROLE',
179
+ id: roleName,
180
+ trn: iamRole.trn,
181
+ attributes: { ...iamRole },
182
+ });
183
+ await delay(IAM_ROLE_PROPAGATION_DELAY_MS);
184
+ }
185
+ const iamRoleInstance = instances.find((i) => i.type === 'VOLCENGINE_IAM_ROLE');
186
+ if (!iamRoleInstance) {
187
+ throw new Error(lang_1.lang.__('IAM_ROLE_INSTANCE_NOT_FOUND', { roleName }));
188
+ }
189
+ const trn = iamRoleInstance.trn ??
190
+ (context.accountId ? `trn:iam::${context.accountId}:role/${iamRoleInstance.id}` : undefined);
191
+ if (!trn) {
192
+ throw new Error(lang_1.lang.__('IAM_ROLE_TRN_MISSING', { roleName }));
193
+ }
194
+ const role = {
195
+ roleName,
196
+ trn,
197
+ };
198
+ return {
199
+ logConfig,
200
+ role,
201
+ instances,
202
+ };
203
+ };
204
+ const deleteDependentResources = async (context, instances) => {
205
+ const client = (0, volcengineClient_1.createVolcengineClient)(context);
206
+ for (const instance of [...instances].reverse()) {
207
+ try {
208
+ switch (instance.type) {
209
+ case 'VOLCENGINE_TLS_INDEX': {
210
+ const [projectName, topicName] = instance.id.split('/');
211
+ logger_1.logger.info(lang_1.lang.__('DELETING_TLS_INDEX', { id: instance.id }));
212
+ await client.tls.deleteIndex(projectName, topicName);
213
+ break;
214
+ }
215
+ case 'VOLCENGINE_TLS_TOPIC': {
216
+ const [projectName, topicName] = instance.id.split('/');
217
+ logger_1.logger.info(lang_1.lang.__('DELETING_TLS_TOPIC', { id: instance.id }));
218
+ await client.tls.deleteTopic(projectName, topicName);
219
+ break;
220
+ }
221
+ case 'VOLCENGINE_TLS_PROJECT':
222
+ logger_1.logger.info(lang_1.lang.__('DELETING_TLS_PROJECT', { id: instance.id }));
223
+ await client.tls.deleteProject(instance.id);
224
+ break;
225
+ case 'VOLCENGINE_IAM_ROLE':
226
+ logger_1.logger.info(lang_1.lang.__('DELETING_IAM_ROLE', { id: instance.id }));
227
+ await client.iam.deleteRole(instance.id);
228
+ break;
229
+ default:
230
+ logger_1.logger.warn(lang_1.lang.__('UNKNOWN_RESOURCE_TYPE', { type: instance.type }));
231
+ }
232
+ }
233
+ catch (err) {
234
+ logger_1.logger.error(lang_1.lang.__('FAILED_TO_DELETE_RESOURCE', {
235
+ type: instance.type,
236
+ id: instance.id,
237
+ error: String(err),
238
+ }));
239
+ }
240
+ }
241
+ };
242
+ const createResource = async (context, fn, state) => {
243
+ const logicalId = `functions.${fn.key}`;
244
+ const serviceName = `${context.app}-${context.service}`;
245
+ const existingResourceState = (0, common_1.getResource)(state, logicalId);
246
+ const existingDependentInstances = (existingResourceState?.instances ?? []).filter((i) => i.type !== 'VOLCENGINE_VEFAAS_FUNCTION');
247
+ const dependentResources = await createDependentResources(context, fn, serviceName, existingDependentInstances);
248
+ const config = (0, vefaasTypes_1.functionToVefaasConfig)(fn, {
249
+ role: dependentResources.role?.trn,
250
+ logConfig: dependentResources.logConfig,
251
+ vpcConfig: fn.network
252
+ ? {
253
+ vpcId: fn.network.vpc_id,
254
+ subnetIds: fn.network.subnet_ids,
255
+ securityGroupIds: fn.network.security_group?.name ? [fn.network.security_group.name] : [],
256
+ }
257
+ : undefined,
258
+ tosMountConfig: fn.storage?.nas?.[0]
259
+ ? {
260
+ bucketName: fn.storage.nas[0]?.bucket_name || '',
261
+ mountPath: fn.storage.nas[0].mount_path,
262
+ }
263
+ : undefined,
264
+ });
265
+ const codePath = fn.code.path;
266
+ const codeHash = (0, common_1.computeFileHash)(codePath);
267
+ const definition = (0, vefaasTypes_1.extractVefaasDefinition)(config, codeHash);
268
+ const sid = (0, common_1.buildSid)('volcengine', context.service, context.stage, fn.name);
269
+ const dependentInstances = dependentResources.instances.map((dep) => ({
270
+ sid: dep.sid ??
271
+ (0, common_1.buildSid)('volcengine', dep.type.replace('VOLCENGINE_', '').toLowerCase(), context.stage, dep.id),
272
+ id: dep.id,
273
+ type: dep.type,
274
+ ...(dep.trn ? { trn: dep.trn } : {}),
275
+ ...dep.attributes,
276
+ }));
277
+ const taintedResourceState = {
278
+ mode: 'managed',
279
+ region: context.region,
280
+ definition,
281
+ instances: [
282
+ {
283
+ type: 'VOLCENGINE_VEFAAS_FUNCTION',
284
+ sid,
285
+ id: fn.name,
286
+ functionName: fn.name,
287
+ attributes: {},
288
+ },
289
+ ...dependentInstances,
290
+ ],
291
+ status: 'tainted',
292
+ lastUpdated: new Date().toISOString(),
293
+ };
294
+ const stateAfterDependents = (0, common_1.setResource)(state, logicalId, taintedResourceState);
295
+ const client = (0, volcengineClient_1.createVolcengineClient)(context);
296
+ const isTainted = existingResourceState?.status === 'tainted';
297
+ const existingFunctionOnRetry = isTainted ? await client.vefaas.getFunction(fn.name) : null;
298
+ if (existingFunctionOnRetry) {
299
+ logger_1.logger.info(lang_1.lang.__('VEFAAS_FUNCTION_EXISTS_RECOVERY', { functionName: fn.name }));
300
+ }
301
+ try {
302
+ if (!existingFunctionOnRetry) {
303
+ const codeContent = await Promise.resolve().then(() => __importStar(require('node:fs'))).then((fs) => fs.readFileSync(codePath));
304
+ const codeBase64 = codeContent.toString('base64');
305
+ await client.vefaas.createFunction(config, codeBase64);
306
+ }
307
+ }
308
+ catch (error) {
309
+ if (isRecoverableCreateError(error)) {
310
+ await delay(RECOVERY_GET_FUNCTION_DELAY_MS);
311
+ const functionAfterError = await client.vefaas.getFunction(fn.name);
312
+ if (!functionAfterError) {
313
+ throw new types_1.PartialResourceError(stateAfterDependents, error);
314
+ }
315
+ }
316
+ else {
317
+ throw new types_1.PartialResourceError(stateAfterDependents, error);
318
+ }
319
+ }
320
+ const functionInfo = await client.vefaas.getFunction(fn.name);
321
+ if (!functionInfo) {
322
+ throw new Error(lang_1.lang.__('RESOURCE_NOT_FOUND_PROVIDER', { resourceType: 'Function', name: fn.name }));
323
+ }
324
+ const vefaasInstance = buildVefaasInstanceFromProvider(functionInfo, sid);
325
+ const resourceState = {
326
+ mode: 'managed',
327
+ region: context.region,
328
+ definition,
329
+ instances: [vefaasInstance, ...dependentInstances],
330
+ status: 'ready',
331
+ lastUpdated: new Date().toISOString(),
332
+ };
333
+ return (0, common_1.setResource)(stateAfterDependents, logicalId, resourceState);
334
+ };
335
+ exports.createResource = createResource;
336
+ const readResource = async (context, functionName) => {
337
+ const client = (0, volcengineClient_1.createVolcengineClient)(context);
338
+ return await client.vefaas.getFunction(functionName);
339
+ };
340
+ exports.readResource = readResource;
341
+ const updateResource = async (context, fn, state) => {
342
+ const serviceName = `${context.app}-${context.service}`;
343
+ const logicalId = `functions.${fn.key}`;
344
+ const currentState = (0, common_1.getResource)(state, logicalId);
345
+ if (!currentState) {
346
+ throw new Error(lang_1.lang.__('RESOURCE_STATE_NOT_FOUND', { logicalId }));
347
+ }
348
+ const currentInstance = currentState.instances.find((i) => i.type === 'VOLCENGINE_VEFAAS_FUNCTION');
349
+ if (!currentInstance) {
350
+ throw new Error(lang_1.lang.__('RESOURCE_INSTANCE_NOT_FOUND', { logicalId }));
351
+ }
352
+ const existingInstances = (currentState.instances ?? []);
353
+ const isTainted = currentState.status === 'tainted';
354
+ const hasTlsResources = existingInstances.some((i) => i.type === 'VOLCENGINE_TLS_PROJECT');
355
+ const hasIamRole = existingInstances.some((i) => i.type === 'VOLCENGINE_IAM_ROLE');
356
+ const client = (0, volcengineClient_1.createVolcengineClient)(context);
357
+ const newDependentInstances = [];
358
+ let logConfig;
359
+ let role;
360
+ if (fn.log && !hasTlsResources && !isTainted) {
361
+ const deps = await createDependentResources(context, { ...fn, network: undefined, storage: { disk: undefined, nas: undefined } }, serviceName);
362
+ logConfig = deps.logConfig;
363
+ newDependentInstances.push(...deps.instances.filter((i) => i.type.startsWith('VOLCENGINE_TLS_')));
364
+ }
365
+ else if (hasTlsResources) {
366
+ const tlsProjectInstance = existingInstances.find((i) => i.type === 'VOLCENGINE_TLS_PROJECT');
367
+ const tlsTopicInstance = existingInstances.find((i) => i.type === 'VOLCENGINE_TLS_TOPIC');
368
+ if (tlsProjectInstance && tlsTopicInstance) {
369
+ const [projectName, topicName] = tlsTopicInstance.id.split('/');
370
+ logConfig = { project: projectName, topic: topicName };
371
+ }
372
+ }
373
+ if (!hasIamRole && !isTainted) {
374
+ const deps = await createDependentResources(context, { ...fn, log: false, network: undefined, storage: { disk: undefined, nas: undefined } }, serviceName);
375
+ role = deps.role;
376
+ newDependentInstances.push(...deps.instances.filter((i) => i.type === 'VOLCENGINE_IAM_ROLE'));
377
+ }
378
+ else {
379
+ const iamRoleInstance = existingInstances.find((i) => i.type === 'VOLCENGINE_IAM_ROLE');
380
+ if (iamRoleInstance) {
381
+ const trn = iamRoleInstance.trn ??
382
+ (context.accountId
383
+ ? `trn:iam::${context.accountId}:role/${iamRoleInstance.id}`
384
+ : undefined);
385
+ if (!trn) {
386
+ throw new Error(lang_1.lang.__('IAM_ROLE_TRN_MISSING', { roleName: iamRoleInstance.id }));
387
+ }
388
+ role = {
389
+ roleName: iamRoleInstance.id,
390
+ trn,
391
+ };
392
+ const trustedServices = (0, vefaasTypes_1.getTrustedServicesForFunction)(fn, context);
393
+ await client.iam.updateRoleTrustPolicy(iamRoleInstance.id, (0, vefaasTypes_1.buildDefaultTrustPolicy)(trustedServices));
394
+ }
395
+ }
396
+ const config = (0, vefaasTypes_1.functionToVefaasConfig)(fn, {
397
+ role: role?.trn,
398
+ logConfig,
399
+ vpcConfig: fn.network
400
+ ? {
401
+ vpcId: fn.network.vpc_id,
402
+ subnetIds: fn.network.subnet_ids,
403
+ securityGroupIds: fn.network.security_group?.name ? [fn.network.security_group.name] : [],
404
+ }
405
+ : undefined,
406
+ tosMountConfig: fn.storage?.nas?.[0]
407
+ ? {
408
+ bucketName: fn.storage.nas[0]?.bucket_name || '',
409
+ mountPath: fn.storage.nas[0].mount_path,
410
+ }
411
+ : undefined,
412
+ });
413
+ const codePath = fn.code.path;
414
+ const desiredCodeHash = (0, common_1.computeFileHash)(codePath);
415
+ const desiredDefinition = (0, vefaasTypes_1.extractVefaasDefinition)(config, desiredCodeHash);
416
+ const currentDefinition = currentState.definition || {};
417
+ const currentCodeHash = currentDefinition.codeHash;
418
+ const codeChanged = currentCodeHash !== desiredCodeHash;
419
+ const existingConfigOnly = {
420
+ runtime: currentDefinition.runtime,
421
+ handler: currentDefinition.handler,
422
+ memorySize: currentDefinition.memorySize,
423
+ timeout: currentDefinition.timeout,
424
+ environment: currentDefinition.environment,
425
+ description: currentDefinition.description,
426
+ role: currentDefinition.role,
427
+ vpcConfig: currentDefinition.vpcConfig,
428
+ tosMountConfig: currentDefinition.tosMountConfig,
429
+ logConfig: currentDefinition.logConfig,
430
+ };
431
+ const desiredConfigOnly = {
432
+ runtime: desiredDefinition.runtime,
433
+ handler: desiredDefinition.handler,
434
+ memorySize: desiredDefinition.memorySize,
435
+ timeout: desiredDefinition.timeout,
436
+ environment: desiredDefinition.environment,
437
+ description: desiredDefinition.description,
438
+ role: desiredDefinition.role,
439
+ vpcConfig: desiredDefinition.vpcConfig,
440
+ tosMountConfig: desiredDefinition.tosMountConfig,
441
+ logConfig: desiredDefinition.logConfig,
442
+ };
443
+ const configChanged = !(0, common_1.attributesEqual)(existingConfigOnly, desiredConfigOnly);
444
+ if (configChanged) {
445
+ await client.vefaas.updateFunctionConfiguration(config);
446
+ }
447
+ if (codeChanged) {
448
+ const codeContent = await Promise.resolve().then(() => __importStar(require('node:fs'))).then((fs) => fs.readFileSync(codePath));
449
+ const codeBase64 = codeContent.toString('base64');
450
+ await client.vefaas.updateFunctionCode(fn.name, codeBase64);
451
+ }
452
+ const functionInfo = await client.vefaas.getFunction(fn.name);
453
+ if (!functionInfo) {
454
+ throw new Error(lang_1.lang.__('RESOURCE_NOT_FOUND_PROVIDER', { resourceType: 'Function', name: fn.name }));
455
+ }
456
+ const sid = currentInstance.sid ?? (0, common_1.buildSid)('volcengine', context.service, context.stage, fn.name);
457
+ const vefaasInstance = buildVefaasInstanceFromProvider(functionInfo, sid);
458
+ const existingDependentInstances = existingInstances
459
+ .filter((i) => i.type !== 'VOLCENGINE_VEFAAS_FUNCTION')
460
+ .map((i) => {
461
+ const { sid: existingSid, id: existingId, ...rest } = i;
462
+ return {
463
+ sid: existingSid ??
464
+ (0, common_1.buildSid)('volcengine', i.type?.toString().replace('VOLCENGINE_', '').toLowerCase() ?? '', context.stage, existingId?.toString() ?? ''),
465
+ id: existingId?.toString() ?? '',
466
+ ...rest,
467
+ };
468
+ });
469
+ const newDependentInstancesMapped = newDependentInstances.map((dep) => ({
470
+ sid: dep.sid ??
471
+ (0, common_1.buildSid)('volcengine', dep.type.replace('VOLCENGINE_', '').toLowerCase(), context.stage, dep.id),
472
+ id: dep.id,
473
+ type: dep.type,
474
+ ...(dep.trn ? { trn: dep.trn } : {}),
475
+ ...dep.attributes,
476
+ }));
477
+ const resourceState = {
478
+ mode: 'managed',
479
+ region: context.region,
480
+ definition: desiredDefinition,
481
+ instances: [vefaasInstance, ...existingDependentInstances, ...newDependentInstancesMapped],
482
+ status: 'ready',
483
+ lastUpdated: new Date().toISOString(),
484
+ };
485
+ return (0, common_1.setResource)(state, logicalId, resourceState);
486
+ };
487
+ exports.updateResource = updateResource;
488
+ const deleteResource = async (context, functionName, logicalId, state) => {
489
+ const existingState = (0, common_1.getResource)(state, logicalId);
490
+ const existingInstances = (existingState?.instances ?? []);
491
+ const hasVefaasFunction = existingInstances.some((i) => i.type === 'VOLCENGINE_VEFAAS_FUNCTION');
492
+ const client = (0, volcengineClient_1.createVolcengineClient)(context);
493
+ if (hasVefaasFunction) {
494
+ try {
495
+ await client.vefaas.deleteFunction(functionName);
496
+ }
497
+ catch (err) {
498
+ const errorCode = err?.code;
499
+ if (errorCode === 'FunctionNotFound' || errorCode === 'ResourceNotFound') {
500
+ logger_1.logger.warn(lang_1.lang.__('RESOURCE_NOT_FOUND_PROVIDER', { resourceType: 'Function', name: functionName }));
501
+ }
502
+ else {
503
+ throw err;
504
+ }
505
+ }
506
+ }
507
+ const dependentInstances = existingInstances.filter((i) => i.type !== 'VOLCENGINE_VEFAAS_FUNCTION' && !i.type.includes('undefined'));
508
+ if (dependentInstances.length > 0) {
509
+ await deleteDependentResources(context, dependentInstances);
510
+ }
511
+ return (0, common_1.removeResource)(state, logicalId);
512
+ };
513
+ exports.deleteResource = deleteResource;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTrustedServicesForFunction = exports.buildDefaultTrustPolicy = exports.extractVefaasDefinition = exports.functionToVefaasConfig = void 0;
4
+ /**
5
+ * Determines which Volcengine services should be trusted to assume the function's IAM role.
6
+ *
7
+ * IMPORTANT: `trigger.backend` is expected to be an *unresolved* YAML reference string in the
8
+ * form `${functions.<functionKey>}` (e.g. `${functions.my_fn}`). This function compares against
9
+ * that raw string — NOT against a resolved function name or ARN. If the backend value has already
10
+ * been resolved to a function name, this check will silently return false and `apigateway` will
11
+ * NOT be added to the trust policy, breaking API Gateway invocation.
12
+ */
13
+ const getTrustedServicesForFunction = (fn, context) => {
14
+ const expectedBackendRef = `\${functions.${fn.key}}`;
15
+ const hasApiGateway = context.iac?.events?.some((event) => event.triggers?.some((trigger) => String(trigger.backend) === expectedBackendRef));
16
+ return hasApiGateway
17
+ ? ['vefaas.volcengine.com', 'apigateway.volcengine.com']
18
+ : ['vefaas.volcengine.com'];
19
+ };
20
+ exports.getTrustedServicesForFunction = getTrustedServicesForFunction;
21
+ const functionToVefaasConfig = (fn, options) => {
22
+ const config = {
23
+ functionName: fn.name,
24
+ runtime: fn.code.runtime,
25
+ handler: fn.code.handler,
26
+ memoryMb: fn.memory ?? 512,
27
+ requestTimeout: fn.timeout ?? 60,
28
+ environmentVariables: fn.environment,
29
+ };
30
+ if (options?.role) {
31
+ config.role = options.role;
32
+ }
33
+ if (options?.vpcConfig) {
34
+ config.vpcConfig = options.vpcConfig;
35
+ }
36
+ if (options?.tosMountConfig) {
37
+ config.tosMountConfig = options.tosMountConfig;
38
+ }
39
+ if (options?.logConfig) {
40
+ config.logConfig = options.logConfig;
41
+ }
42
+ return config;
43
+ };
44
+ exports.functionToVefaasConfig = functionToVefaasConfig;
45
+ const extractVefaasDefinition = (config, codeHash) => {
46
+ return {
47
+ functionName: config.functionName,
48
+ runtime: config.runtime,
49
+ handler: config.handler,
50
+ memorySize: config.memoryMb,
51
+ timeout: config.requestTimeout,
52
+ environment: config.environmentVariables || {},
53
+ codeHash,
54
+ description: config.description,
55
+ role: config.role,
56
+ vpcConfig: config.vpcConfig,
57
+ tosMountConfig: config.tosMountConfig,
58
+ logConfig: config.logConfig,
59
+ };
60
+ };
61
+ exports.extractVefaasDefinition = extractVefaasDefinition;
62
+ const buildDefaultTrustPolicy = (trustedServices) => {
63
+ return {
64
+ Statement: [
65
+ {
66
+ Effect: 'Allow',
67
+ Action: ['sts:AssumeRole'],
68
+ Principal: {
69
+ Service: trustedServices,
70
+ },
71
+ },
72
+ ],
73
+ };
74
+ };
75
+ exports.buildDefaultTrustPolicy = buildDefaultTrustPolicy;
@@ -28,6 +28,9 @@ var ResourceTypeEnum;
28
28
  ResourceTypeEnum["ALIYUN_NAS_ACCESS_GROUP"] = "ALIYUN_NAS_ACCESS_GROUP";
29
29
  ResourceTypeEnum["ALIYUN_NAS_FILE_SYSTEM"] = "ALIYUN_NAS_FILE_SYSTEM";
30
30
  ResourceTypeEnum["ALIYUN_NAS_MOUNT_TARGET"] = "ALIYUN_NAS_MOUNT_TARGET";
31
+ // Volcengine - Primary Resources
32
+ ResourceTypeEnum["VOLCENGINE_VEFAAS_FUNCTION"] = "VOLCENGINE_VEFAAS_FUNCTION";
33
+ ResourceTypeEnum["VOLCENGINE_TOS_BUCKET"] = "VOLCENGINE_TOS_BUCKET";
31
34
  })(ResourceTypeEnum || (exports.ResourceTypeEnum = ResourceTypeEnum = {}));
32
35
  exports.CURRENT_STATE_VERSION = '3.0';
33
36
  class PartialResourceError extends Error {