@cloudbase/manager-node 5.7.1-beta.1 → 5.8.0-beta.1
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/lib/cloudApp/index.js +12 -3
- package/lib/deploy/DatabaseDeployer.js +238 -0
- package/lib/deploy/DeployOrchestrator.js +645 -0
- package/lib/deploy/FunctionDeployer.js +606 -0
- package/lib/deploy/GatewayDeployer.js +612 -0
- package/lib/deploy/StateStore.js +327 -0
- package/lib/deploy/StaticDeployer.js +236 -0
- package/lib/deploy/domain.js +41 -0
- package/lib/deploy/framework.js +230 -0
- package/lib/deploy/function/artifact.js +214 -0
- package/lib/deploy/function/builders/cloud.js +779 -0
- package/lib/deploy/function/cam-preflight.js +173 -0
- package/lib/deploy/function/cloud-build-service.js +191 -0
- package/lib/deploy/function/config-guard.js +595 -0
- package/lib/deploy/function/docker-preflight.js +87 -0
- package/lib/deploy/function/image-preflight.js +164 -0
- package/lib/deploy/function/local-builder.js +129 -0
- package/lib/deploy/function/planner.js +278 -0
- package/lib/deploy/function/preflight.js +329 -0
- package/lib/deploy/types.js +107 -0
- package/lib/env/index.js +0 -27
- package/lib/environment.js +11 -0
- package/lib/function/index.js +30 -4
- package/lib/hosting/index.js +4 -1
- package/lib/index.js +31 -0
- package/lib/projectValidator/index.js +452 -0
- package/lib/projectValidator/types.js +2 -0
- package/lib/storage/index.js +6 -7
- package/lib/utils/index.js +62 -5
- package/package.json +2 -1
- package/types/cloudApp/index.d.ts +4 -0
- package/types/cloudApp/types.d.ts +87 -6
- package/types/deploy/DatabaseDeployer.d.ts +70 -0
- package/types/deploy/DeployOrchestrator.d.ts +170 -0
- package/types/deploy/FunctionDeployer.d.ts +161 -0
- package/types/deploy/GatewayDeployer.d.ts +194 -0
- package/types/deploy/StateStore.d.ts +170 -0
- package/types/deploy/StaticDeployer.d.ts +97 -0
- package/types/deploy/domain.d.ts +17 -0
- package/types/deploy/framework.d.ts +63 -0
- package/types/deploy/function/artifact.d.ts +40 -0
- package/types/deploy/function/builders/cloud.d.ts +110 -0
- package/types/deploy/function/cam-preflight.d.ts +47 -0
- package/types/deploy/function/cloud-build-service.d.ts +10 -0
- package/types/deploy/function/config-guard.d.ts +41 -0
- package/types/deploy/function/docker-preflight.d.ts +17 -0
- package/types/deploy/function/image-preflight.d.ts +21 -0
- package/types/deploy/function/local-builder.d.ts +26 -0
- package/types/deploy/function/planner.d.ts +15 -0
- package/types/deploy/function/preflight.d.ts +9 -0
- package/types/deploy/types.d.ts +433 -0
- package/types/env/index.d.ts +1 -17
- package/types/env/type.d.ts +6 -260
- package/types/environment.d.ts +7 -0
- package/types/function/index.d.ts +2 -0
- package/types/function/types.d.ts +15 -0
- package/types/hosting/index.d.ts +6 -0
- package/types/index.d.ts +18 -0
- package/types/interfaces/function.interface.d.ts +1 -1
- package/types/projectValidator/index.d.ts +38 -0
- package/types/projectValidator/types.d.ts +26 -0
- package/types/storage/index.d.ts +6 -0
- package/types/utils/index.d.ts +13 -0
|
@@ -0,0 +1,606 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.IMAGE_PULL_GRANT_URL = exports.REQUIRED_IMAGE_PULL_POLICY = exports.SCF_PULL_IMAGE_ROLE = exports.isCamPermissionError = exports.runImagePullAuthPreflight = exports.runImageRemotePreflight = exports.runLocalPreflight = exports.createCloudBuildService = exports.buildImageOnCloud = exports.toCloudFunction = exports.createImageArtifactFromConfig = exports.createCodeArtifact = exports.toCurrentState = exports.createDeployPlan = exports.parseImageReference = exports.normalizeGatewayPath = exports.normalizeDeployConfig = exports.isCustomImageRuntime = exports.checkDeployConfig = exports.assertDeployConfig = exports.FunctionDeployer = void 0;
|
|
18
|
+
const error_1 = require("../error");
|
|
19
|
+
const artifact_1 = require("./function/artifact");
|
|
20
|
+
const config_guard_1 = require("./function/config-guard");
|
|
21
|
+
const cam_preflight_1 = require("./function/cam-preflight");
|
|
22
|
+
const cloud_1 = require("./function/builders/cloud");
|
|
23
|
+
const cloud_build_service_1 = require("./function/cloud-build-service");
|
|
24
|
+
const docker_preflight_1 = require("./function/docker-preflight");
|
|
25
|
+
const GatewayDeployer_1 = require("./GatewayDeployer");
|
|
26
|
+
const domain_1 = require("./domain");
|
|
27
|
+
const image_preflight_1 = require("./function/image-preflight");
|
|
28
|
+
const local_builder_1 = require("./function/local-builder");
|
|
29
|
+
const planner_1 = require("./function/planner");
|
|
30
|
+
const preflight_1 = require("./function/preflight");
|
|
31
|
+
const types_1 = require("./types");
|
|
32
|
+
/** OPA 用户策略 key */
|
|
33
|
+
const AUTHZ_USER_REGO_KEY = 'authz.user.rego';
|
|
34
|
+
/**
|
|
35
|
+
* 函数部署编排器
|
|
36
|
+
*
|
|
37
|
+
* 统一编排 Event 与 HTTP 函数的部署流程,对上层屏蔽以下差异:
|
|
38
|
+
* - Event 与 HTTP 的运行模型
|
|
39
|
+
* - zip、cloud、local、image 四种构建策略
|
|
40
|
+
* - 创建、更新与不可原地更新的场景识别
|
|
41
|
+
*
|
|
42
|
+
* 已支持代码包、已有镜像、云端构建、本地构建、匿名访问与网关路径收敛;
|
|
43
|
+
* 函数类型、运行时或协议等创建期字段变化时不会自动删除函数,而是引导用户
|
|
44
|
+
* 在控制台确认并手工删除后重新部署。local 企业版 TCR 等未实现能力会显式报错。
|
|
45
|
+
*/
|
|
46
|
+
class FunctionDeployer {
|
|
47
|
+
constructor(environment, deps) {
|
|
48
|
+
var _a;
|
|
49
|
+
this.environment = environment;
|
|
50
|
+
this.functionService = environment.getFunctionService();
|
|
51
|
+
this.imagePreflightDeps = deps === null || deps === void 0 ? void 0 : deps.imagePreflightDeps;
|
|
52
|
+
this.cloudBuildServiceFactory =
|
|
53
|
+
(_a = deps === null || deps === void 0 ? void 0 : deps.cloudBuildServiceFactory) !== null && _a !== void 0 ? _a : (() => (0, cloud_build_service_1.createCloudBuildService)(this.environment.getCloudAppService()));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 兼容既有声明式部署入口
|
|
57
|
+
*
|
|
58
|
+
* 该方法只负责补齐声明式上下文并委托 deployFunction,避免继续维护第二套
|
|
59
|
+
* cloud/local/image 分发逻辑。force 在旧入口中表示允许覆盖,计划阶段会自行
|
|
60
|
+
* 判断 create/update;不可原地更新的变更始终要求用户在控制台手工删除函数。
|
|
61
|
+
*/
|
|
62
|
+
async deploy(options) {
|
|
63
|
+
const { func, functionRootPath, functionPath } = options;
|
|
64
|
+
const config = this.withLegacyDeployContext(func, functionRootPath, functionPath);
|
|
65
|
+
const result = await this.deployFunction(config);
|
|
66
|
+
return {
|
|
67
|
+
name: result.functionName,
|
|
68
|
+
url: await this.resolveUrl(config)
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** 解析最终部署策略:显式 buildStrategy 优先,未配置默认 zip */
|
|
72
|
+
resolveStrategy(config) {
|
|
73
|
+
return config.type === 'HTTP' && config.buildStrategy
|
|
74
|
+
? config.buildStrategy
|
|
75
|
+
: 'zip';
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 配置 HTTP 函数匿名访问(public=true)
|
|
79
|
+
* 通过 OPA Rego 策略追加匿名放通规则,不覆盖用户已有策略
|
|
80
|
+
*/
|
|
81
|
+
async ensurePublicAccess(functionName) {
|
|
82
|
+
const permissionService = this.environment.getPermissionService();
|
|
83
|
+
const { Item } = await permissionService.describeEnvAuthzConfig({
|
|
84
|
+
key: AUTHZ_USER_REGO_KEY
|
|
85
|
+
});
|
|
86
|
+
const currentRego = (Item === null || Item === void 0 ? void 0 : Item.Value) || '';
|
|
87
|
+
if (currentRego.includes(`resource_name == "${functionName}"`)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const rule = this.buildPublicAccessRego(functionName);
|
|
91
|
+
const mergedRego = currentRego.trim() ? `${currentRego.trim()}\n\n${rule}` : rule;
|
|
92
|
+
await permissionService.modifyEnvAuthzConfig({
|
|
93
|
+
key: AUTHZ_USER_REGO_KEY,
|
|
94
|
+
value: mergedRego
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 收敛 HTTP 函数网关路由(gatewayPath 配置)
|
|
99
|
+
*
|
|
100
|
+
* 幂等:云端不存在该 path → 创建;已存在且一致 → 跳过;不一致 → 更新。
|
|
101
|
+
* 复用 GatewayDeployer 的幂等收敛与默认域名解析逻辑,与 gateway.routes 声明式部署行为一致。
|
|
102
|
+
* 返回该函数网关访问 URL(与 resolveUrl 解析结果一致),无 envId 时返回 undefined。
|
|
103
|
+
*/
|
|
104
|
+
async ensureGatewayRoute(config) {
|
|
105
|
+
var _a;
|
|
106
|
+
if (config.type !== 'HTTP' || !config.gatewayPath) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
const envId = this.environment.getEnvId();
|
|
110
|
+
if (!envId) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
const result = await new GatewayDeployer_1.GatewayDeployer(this.environment).deploy({
|
|
114
|
+
routes: [
|
|
115
|
+
{
|
|
116
|
+
path: config.gatewayPath,
|
|
117
|
+
target: `function:${config.name}`
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
envId
|
|
121
|
+
});
|
|
122
|
+
return (_a = result.routes.find(route => route.path === config.gatewayPath)) === null || _a === void 0 ? void 0 : _a.url;
|
|
123
|
+
}
|
|
124
|
+
/** 生成匿名访问 OPA Rego 规则 */
|
|
125
|
+
buildPublicAccessRego(functionName) {
|
|
126
|
+
return [
|
|
127
|
+
`# 自动生成:放通匿名用户访问云函数 ${functionName}(public: true)`,
|
|
128
|
+
'allow if {',
|
|
129
|
+
' input.cloudbase.resource_type == "functions"',
|
|
130
|
+
` input.cloudbase.resource_name == "${functionName}"`,
|
|
131
|
+
' input.subject.auth_type == "anonymous"',
|
|
132
|
+
'}'
|
|
133
|
+
].join('\n');
|
|
134
|
+
}
|
|
135
|
+
/** 生成 HTTP 函数可访问 URL */
|
|
136
|
+
async resolveUrl(config) {
|
|
137
|
+
if (config.type !== 'HTTP') {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
let envId;
|
|
141
|
+
try {
|
|
142
|
+
envId = this.environment.getEnvId();
|
|
143
|
+
}
|
|
144
|
+
catch (_a) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
if (!envId) {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
const pathValue = config.path || config.gatewayPath;
|
|
151
|
+
// 推荐方式:通过 API 网关调用云函数(无需配置访问服务)
|
|
152
|
+
const recommendedUrl = `${(0, domain_1.getGatewayBaseUrl)(this.environment, envId)}/v1/functions/${config.name}`;
|
|
153
|
+
if (!pathValue) {
|
|
154
|
+
return recommendedUrl;
|
|
155
|
+
}
|
|
156
|
+
// 配了访问服务(path / gatewayPath)→ 环境 HTTP 访问服务默认域名 + 路径
|
|
157
|
+
const domain = await (0, domain_1.resolveAccessServiceDomain)(this.environment, envId);
|
|
158
|
+
if (domain) {
|
|
159
|
+
return `https://${domain}/${pathValue.replace(/^\//, '')}`;
|
|
160
|
+
}
|
|
161
|
+
// getEnvInfo 失败时退化为推荐方式,保证地址可访问
|
|
162
|
+
return recommendedUrl;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* 校验部署配置
|
|
166
|
+
*
|
|
167
|
+
* 只做纯函数、无 IO 的配置契约校验,一次返回全部问题
|
|
168
|
+
*/
|
|
169
|
+
checkConfig(config) {
|
|
170
|
+
return (0, config_guard_1.checkDeployConfig)(config);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 生成部署计划
|
|
174
|
+
*
|
|
175
|
+
* 会查询线上函数状态,用于判断创建、更新或识别不可原地更新的变更
|
|
176
|
+
*/
|
|
177
|
+
async plan(config) {
|
|
178
|
+
const normalized = (0, config_guard_1.assertDeployConfig)(config);
|
|
179
|
+
const current = await this.getCurrentFunction(normalized.name, normalized.codeSecret);
|
|
180
|
+
return (0, planner_1.createDeployPlan)(normalized, current);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* 部署前本地环境检查(有 IO)
|
|
184
|
+
*
|
|
185
|
+
* 与 checkConfig 不同,本方法会读取文件系统,覆盖 HTTP 代码包和 cloud 镜像构建
|
|
186
|
+
* 所需的本地文件条件。先做配置契约校验,再执行本地检查。
|
|
187
|
+
*/
|
|
188
|
+
preflight(config) {
|
|
189
|
+
const normalized = (0, config_guard_1.assertDeployConfig)(config);
|
|
190
|
+
return (0, preflight_1.runLocalPreflight)(normalized);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 部署函数
|
|
194
|
+
* @param config 部署配置
|
|
195
|
+
* @param options 部署选项,dryRun 时只生成计划不做任何变更
|
|
196
|
+
*/
|
|
197
|
+
async deployFunction(config, options = {}) {
|
|
198
|
+
const startTime = Date.now();
|
|
199
|
+
const ctx = { steps: [], options };
|
|
200
|
+
const warnings = [];
|
|
201
|
+
const report = (0, config_guard_1.checkDeployConfig)(config);
|
|
202
|
+
this.appendWarnings(warnings, report);
|
|
203
|
+
const normalized = await this.runStep(ctx, 'validate', async () => {
|
|
204
|
+
return (0, config_guard_1.assertDeployConfig)(config);
|
|
205
|
+
});
|
|
206
|
+
const plan = await this.runStep(ctx, 'plan', async () => {
|
|
207
|
+
const current = await this.getCurrentFunction(normalized.name, normalized.codeSecret);
|
|
208
|
+
return (0, planner_1.createDeployPlan)(normalized, current);
|
|
209
|
+
});
|
|
210
|
+
if (options.dryRun) {
|
|
211
|
+
return await this.buildResult({
|
|
212
|
+
config: normalized,
|
|
213
|
+
plan,
|
|
214
|
+
steps: ctx.steps,
|
|
215
|
+
warnings,
|
|
216
|
+
startTime,
|
|
217
|
+
dryRun: true
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
// 不可原地更新的变更必须在任何可能产生副作用的 Preflight 之前中止。
|
|
221
|
+
// 尤其避免镜像拉取授权自动补齐等云端写操作先发生,再提示用户手工删除函数。
|
|
222
|
+
this.assertPlanExecutable(plan);
|
|
223
|
+
await this.runStep(ctx, 'preflight', async () => {
|
|
224
|
+
const report = (0, preflight_1.runLocalPreflight)(normalized);
|
|
225
|
+
this.appendWarnings(warnings, report);
|
|
226
|
+
this.assertPreflightReady(report);
|
|
227
|
+
});
|
|
228
|
+
await this.runStep(ctx, 'preflight', async () => {
|
|
229
|
+
const report = await (0, cam_preflight_1.runImagePullAuthPreflight)(this.createCamPreflightAdapter(), normalized, options.autoGrant !== false);
|
|
230
|
+
this.appendWarnings(warnings, report);
|
|
231
|
+
this.assertPreflightReady(report);
|
|
232
|
+
});
|
|
233
|
+
await this.runStep(ctx, 'preflight', async () => {
|
|
234
|
+
const report = await (0, image_preflight_1.runImageRemotePreflight)(normalized, this.resolveDeployRegion(), this.imagePreflightDeps);
|
|
235
|
+
this.appendWarnings(warnings, report);
|
|
236
|
+
this.assertPreflightReady(report);
|
|
237
|
+
});
|
|
238
|
+
// noop:函数本身无变更,但仍可能需要收敛访问配置
|
|
239
|
+
if (plan.action === 'noop') {
|
|
240
|
+
await this.applyAccessPlan(ctx, normalized, plan);
|
|
241
|
+
return await this.buildResult({
|
|
242
|
+
config: normalized,
|
|
243
|
+
plan,
|
|
244
|
+
steps: ctx.steps,
|
|
245
|
+
warnings,
|
|
246
|
+
startTime,
|
|
247
|
+
dryRun: false
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
const artifact = await this.prepareArtifact(ctx, normalized, warnings);
|
|
251
|
+
await this.applyPlan(ctx, { config: normalized, plan, artifact });
|
|
252
|
+
await this.applyAccessPlan(ctx, normalized, plan);
|
|
253
|
+
return await this.buildResult({
|
|
254
|
+
config: normalized,
|
|
255
|
+
plan,
|
|
256
|
+
steps: ctx.steps,
|
|
257
|
+
warnings,
|
|
258
|
+
startTime,
|
|
259
|
+
dryRun: false,
|
|
260
|
+
artifact
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* 将既有声明式调用补齐为完整部署配置
|
|
265
|
+
*/
|
|
266
|
+
withLegacyDeployContext(func, functionRootPath, functionPath) {
|
|
267
|
+
var _a, _b;
|
|
268
|
+
const config = Object.assign({}, func);
|
|
269
|
+
const strategy = this.resolveStrategy(config);
|
|
270
|
+
if (functionPath) {
|
|
271
|
+
config.functionPath = functionPath;
|
|
272
|
+
delete config.functionRootPath;
|
|
273
|
+
}
|
|
274
|
+
else if (functionRootPath && !config.functionRootPath && !config.functionPath) {
|
|
275
|
+
config.functionRootPath = functionRootPath;
|
|
276
|
+
}
|
|
277
|
+
if (strategy === 'cloud' || strategy === 'local') {
|
|
278
|
+
const effectiveFunctionRootPath = functionRootPath || config.functionRootPath;
|
|
279
|
+
const buildCwd = functionPath ||
|
|
280
|
+
(functionRootPath ? `${functionRootPath}/${config.name}` : undefined) ||
|
|
281
|
+
config.functionPath ||
|
|
282
|
+
(effectiveFunctionRootPath
|
|
283
|
+
? `${effectiveFunctionRootPath}/${config.name}`
|
|
284
|
+
: undefined);
|
|
285
|
+
if (buildCwd || ((_a = config.build) === null || _a === void 0 ? void 0 : _a.cwd)) {
|
|
286
|
+
config.build = Object.assign(Object.assign({}, (config.build || {})), { cwd: ((_b = config.build) === null || _b === void 0 ? void 0 : _b.cwd) || buildCwd });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// 兼容旧声明式 imageUri 字段,统一转换到运行时 imageConfig 契约
|
|
290
|
+
if (strategy === 'image' && config.imageUri && !config.imageConfig) {
|
|
291
|
+
config.imageType = config.imageType || 'personal';
|
|
292
|
+
config.imageConfig = {
|
|
293
|
+
imageUri: config.imageUri
|
|
294
|
+
};
|
|
295
|
+
delete config.imageUri;
|
|
296
|
+
}
|
|
297
|
+
return config;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* 解析函数部署地域,供镜像远端地域校验使用
|
|
301
|
+
*
|
|
302
|
+
* 地域来源于 environment 的鉴权配置;未显式配置时返回 undefined,
|
|
303
|
+
* 由image-preflight 自动跳过地域校验,不误报。
|
|
304
|
+
*/
|
|
305
|
+
resolveDeployRegion() {
|
|
306
|
+
try {
|
|
307
|
+
return this.environment.getAuthConfig().region;
|
|
308
|
+
}
|
|
309
|
+
catch (_a) {
|
|
310
|
+
return undefined;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* 构造镜像拉取授权 Preflight 所需的 CAM 适配器
|
|
315
|
+
*
|
|
316
|
+
* 用 CommonService 走 CAM 通道:ListAttachedRolePolicies 读已绑策略、
|
|
317
|
+
* AttachRolePolicies 按策略名绑定。任何权限/网络错误都原样抛出,
|
|
318
|
+
* 交由 cam-preflight 判定并降级,本层不吞异常。
|
|
319
|
+
*/
|
|
320
|
+
createCamPreflightAdapter() {
|
|
321
|
+
const cam = this.environment.getCommonService('cam', undefined);
|
|
322
|
+
return {
|
|
323
|
+
getRoleWithPolicies: async (roleName) => {
|
|
324
|
+
const res = await cam.call({
|
|
325
|
+
Action: 'ListAttachedRolePolicies',
|
|
326
|
+
Param: { Page: 1, Rp: 200, RoleName: roleName }
|
|
327
|
+
});
|
|
328
|
+
const list = (res === null || res === void 0 ? void 0 : res.List) || [];
|
|
329
|
+
return {
|
|
330
|
+
RoleName: roleName,
|
|
331
|
+
attachedPolicyNames: list
|
|
332
|
+
.map(item => item === null || item === void 0 ? void 0 : item.PolicyName)
|
|
333
|
+
.filter((name) => typeof name === 'string')
|
|
334
|
+
};
|
|
335
|
+
},
|
|
336
|
+
attachPolicyByName: async (roleName, policyName) => {
|
|
337
|
+
await cam.call({
|
|
338
|
+
Action: 'AttachRolePolicies',
|
|
339
|
+
Param: { AttachRoleName: roleName, PolicyName: [policyName] }
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
/** 查询线上函数详情,不存在时返回 null */
|
|
345
|
+
async getCurrentFunction(name, codeSecret) {
|
|
346
|
+
try {
|
|
347
|
+
return await this.functionService.getFunctionDetail(name, codeSecret);
|
|
348
|
+
}
|
|
349
|
+
catch (error) {
|
|
350
|
+
if (this.isFunctionNotFoundError(error)) {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
throw error;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/** 判断是否为函数不存在错误,其他错误必须继续抛出 */
|
|
357
|
+
isFunctionNotFoundError(error) {
|
|
358
|
+
var _a, _b;
|
|
359
|
+
const code = (error === null || error === void 0 ? void 0 : error.code) || ((_b = (_a = error === null || error === void 0 ? void 0 : error.original) === null || _a === void 0 ? void 0 : _a.Error) === null || _b === void 0 ? void 0 : _b.Code) || '';
|
|
360
|
+
return typeof code === 'string' && code.startsWith('ResourceNotFound');
|
|
361
|
+
}
|
|
362
|
+
/** 将 warning 连同可执行修复建议汇入部署结果,避免授权链接在编排层丢失 */
|
|
363
|
+
appendWarnings(warnings, report) {
|
|
364
|
+
report.checks
|
|
365
|
+
.filter(item => item.status === 'warn')
|
|
366
|
+
.forEach(item => {
|
|
367
|
+
const remediation = item.remediation ? `,${item.remediation}` : '';
|
|
368
|
+
warnings.push(`[${item.id}] ${item.summary}${remediation}`);
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* 校验 Preflight 报告,存在 fail 项时聚合抛错
|
|
373
|
+
*/
|
|
374
|
+
assertPreflightReady(report) {
|
|
375
|
+
if (report.ready) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const failures = report.checks.filter(item => item.status === 'fail');
|
|
379
|
+
const message = failures
|
|
380
|
+
.map(item => {
|
|
381
|
+
const remediation = item.remediation ? `,${item.remediation}` : '';
|
|
382
|
+
return `- [${item.id}] ${item.summary}${remediation}`;
|
|
383
|
+
})
|
|
384
|
+
.join('\n');
|
|
385
|
+
throw new error_1.CloudBaseError(`部署前检查未通过:\n${message}`, {
|
|
386
|
+
code: failures[0].id
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* 判断计划是否可以在当前实现范围内执行
|
|
391
|
+
*
|
|
392
|
+
* 未实现的能力必须显式报错,避免用户误认为访问规则或镜像构建已经生效
|
|
393
|
+
*/
|
|
394
|
+
assertPlanExecutable(plan) {
|
|
395
|
+
if (plan.action === 'replace') {
|
|
396
|
+
throw new error_1.CloudBaseError(`函数 ${plan.functionName} 存在无法原地更新的变更:\n` +
|
|
397
|
+
plan.replaceReasons.map(reason => `- ${reason}`).join('\n') +
|
|
398
|
+
'\n部署器不会自动删除已有云函数。请先在云开发控制台确认触发器、访问配置和服务中断影响,手工删除该函数后重新部署。', { code: types_1.FUNCTION_DEPLOY_ERROR.FUNCTION_RECREATE_REQUIRED });
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* 准备部署物
|
|
403
|
+
*
|
|
404
|
+
* cloud 策略在云端构建容器内build+push,产出镜像部署物(不依赖本地 Docker);
|
|
405
|
+
* local 策略需要先做 Docker 本地能力检查,再本地构建并推送镜像,产出镜像部署物;
|
|
406
|
+
* 其余策略走同步的 pack。
|
|
407
|
+
*/
|
|
408
|
+
async prepareArtifact(ctx, config, warnings) {
|
|
409
|
+
if (config.buildStrategy === 'cloud') {
|
|
410
|
+
const cloudConfig = config;
|
|
411
|
+
const buildResult = await this.runStep(ctx, 'build', async () => {
|
|
412
|
+
const cloudBuildService = this.cloudBuildServiceFactory();
|
|
413
|
+
return (0, cloud_1.buildImageOnCloud)(cloudConfig, cloudBuildService, {
|
|
414
|
+
onLog: line => this.emitProgress(ctx.options, {
|
|
415
|
+
stage: 'build',
|
|
416
|
+
status: 'start',
|
|
417
|
+
message: line
|
|
418
|
+
}),
|
|
419
|
+
region: this.resolveDeployRegion()
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
return (0, artifact_1.createImageArtifactFromCloudBuild)(config, buildResult);
|
|
423
|
+
}
|
|
424
|
+
if (config.buildStrategy === 'local') {
|
|
425
|
+
await this.runStep(ctx, 'preflight', async () => {
|
|
426
|
+
const report = await (0, docker_preflight_1.runDockerPreflight)();
|
|
427
|
+
this.appendWarnings(warnings, report);
|
|
428
|
+
this.assertPreflightReady(report);
|
|
429
|
+
});
|
|
430
|
+
const localConfig = config;
|
|
431
|
+
const buildResult = await this.runStep(ctx, 'build', async () => {
|
|
432
|
+
return (0, local_builder_1.buildAndPushImage)(localConfig, line => this.emitProgress(ctx.options, { stage: 'build', status: 'start', message: line }));
|
|
433
|
+
});
|
|
434
|
+
return (0, artifact_1.createImageArtifactFromBuild)(config, buildResult);
|
|
435
|
+
}
|
|
436
|
+
return this.runStep(ctx, 'pack', async () => {
|
|
437
|
+
return this.createArtifact(config);
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
/** 生成部署物 */
|
|
441
|
+
createArtifact(config) {
|
|
442
|
+
if (config.buildStrategy === 'image') {
|
|
443
|
+
return (0, artifact_1.createImageArtifactFromConfig)(config);
|
|
444
|
+
}
|
|
445
|
+
return (0, artifact_1.createCodeArtifact)(config);
|
|
446
|
+
}
|
|
447
|
+
/** 执行部署计划 */
|
|
448
|
+
async applyPlan(ctx, params) {
|
|
449
|
+
const { config, plan, artifact } = params;
|
|
450
|
+
// noop:线上与目标一致,不调用任何 SCF 变更接口
|
|
451
|
+
if (plan.action === 'noop') {
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
const func = (0, artifact_1.toCloudFunction)(config, artifact);
|
|
455
|
+
const codeParams = artifact.kind === 'code'
|
|
456
|
+
? {
|
|
457
|
+
functionRootPath: artifact.functionRootPath,
|
|
458
|
+
functionPath: artifact.functionPath,
|
|
459
|
+
base64Code: artifact.base64Code
|
|
460
|
+
}
|
|
461
|
+
: {};
|
|
462
|
+
if (plan.action === 'create') {
|
|
463
|
+
await this.runStep(ctx, 'deploy-function', async () => {
|
|
464
|
+
await this.functionService.createFunction(Object.assign(Object.assign({ func }, codeParams), { codeSecret: config.codeSecret, deployMode: artifact.deployMode,
|
|
465
|
+
// 计划阶段已确认函数不存在,此处不覆盖同名函数,避免并发场景误删
|
|
466
|
+
force: false }));
|
|
467
|
+
});
|
|
468
|
+
await this.runStep(ctx, 'wait-active', async () => {
|
|
469
|
+
await this.functionService.waitFunctionActive(config.name, config.codeSecret);
|
|
470
|
+
});
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
await this.runStep(ctx, 'deploy-function', async () => {
|
|
474
|
+
await this.functionService.updateFunctionCode(Object.assign(Object.assign({ func }, codeParams), { codeSecret: config.codeSecret, deployMode: artifact.deployMode }));
|
|
475
|
+
});
|
|
476
|
+
await this.runStep(ctx, 'wait-active', async () => {
|
|
477
|
+
await this.functionService.waitFunctionActive(config.name, config.codeSecret);
|
|
478
|
+
});
|
|
479
|
+
await this.runStep(ctx, 'deploy-function', async () => {
|
|
480
|
+
await this.functionService.updateFunctionConfig(func);
|
|
481
|
+
});
|
|
482
|
+
await this.runStep(ctx, 'wait-active', async () => {
|
|
483
|
+
await this.functionService.waitFunctionActive(config.name, config.codeSecret);
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
/** 收敛 HTTP 函数访问配置(匿名访问 + 网关路由) */
|
|
487
|
+
async applyAccessPlan(ctx, config, plan) {
|
|
488
|
+
if (config.type !== 'HTTP') {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
if (config.public === true && plan.operations.includes('configure-access')) {
|
|
492
|
+
await this.runStep(ctx, 'configure-access', async () => {
|
|
493
|
+
await this.ensurePublicAccess(config.name);
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
if (plan.operations.includes('configure-gateway')) {
|
|
497
|
+
await this.runStep(ctx, 'configure-gateway', async () => {
|
|
498
|
+
await this.ensureGatewayRoute(config);
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
/** 执行单个步骤并记录耗时与进度 */
|
|
503
|
+
async runStep(ctx, stage, handler) {
|
|
504
|
+
const startTime = Date.now();
|
|
505
|
+
this.emitProgress(ctx.options, { stage, status: 'start' });
|
|
506
|
+
try {
|
|
507
|
+
const result = await handler();
|
|
508
|
+
ctx.steps.push({ stage, status: 'success', elapsedTime: Date.now() - startTime });
|
|
509
|
+
this.emitProgress(ctx.options, { stage, status: 'success' });
|
|
510
|
+
return result;
|
|
511
|
+
}
|
|
512
|
+
catch (error) {
|
|
513
|
+
ctx.steps.push({
|
|
514
|
+
stage,
|
|
515
|
+
status: 'failed',
|
|
516
|
+
elapsedTime: Date.now() - startTime,
|
|
517
|
+
detail: error === null || error === void 0 ? void 0 : error.message
|
|
518
|
+
});
|
|
519
|
+
this.emitProgress(ctx.options, { stage, status: 'failed', message: error === null || error === void 0 ? void 0 : error.message });
|
|
520
|
+
throw error;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
/** 上报进度,回调异常不影响部署流程 */
|
|
524
|
+
emitProgress(options, event) {
|
|
525
|
+
if (typeof options.onProgress !== 'function') {
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
try {
|
|
529
|
+
options.onProgress(event);
|
|
530
|
+
}
|
|
531
|
+
catch (error) {
|
|
532
|
+
// 进度回调由调用方提供,异常不应影响部署结果
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
/** 汇总部署结果 */
|
|
536
|
+
async buildResult(params) {
|
|
537
|
+
const { config, plan, steps, warnings, startTime, dryRun, artifact } = params;
|
|
538
|
+
const result = {
|
|
539
|
+
functionName: config.name,
|
|
540
|
+
functionType: config.type,
|
|
541
|
+
requestedStrategy: config.buildStrategy,
|
|
542
|
+
effectiveStrategy: (artifact === null || artifact === void 0 ? void 0 : artifact.kind) === 'image' ? artifact.effectiveStrategy : config.buildStrategy,
|
|
543
|
+
action: plan.action,
|
|
544
|
+
operations: plan.operations,
|
|
545
|
+
dryRun,
|
|
546
|
+
elapsedTime: Date.now() - startTime,
|
|
547
|
+
steps,
|
|
548
|
+
warnings,
|
|
549
|
+
plan
|
|
550
|
+
};
|
|
551
|
+
if ((artifact === null || artifact === void 0 ? void 0 : artifact.kind) === 'image') {
|
|
552
|
+
result.imageUri = artifact.imageUri;
|
|
553
|
+
if (artifact.imageDigest !== undefined) {
|
|
554
|
+
result.imageDigest = artifact.imageDigest;
|
|
555
|
+
}
|
|
556
|
+
if (artifact.buildId !== undefined) {
|
|
557
|
+
result.buildId = artifact.buildId;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (config.type === 'HTTP') {
|
|
561
|
+
result.public = config.public;
|
|
562
|
+
result.gatewayUrl = await this.resolveUrl(config);
|
|
563
|
+
}
|
|
564
|
+
if (!dryRun) {
|
|
565
|
+
this.emitProgressDone(params);
|
|
566
|
+
}
|
|
567
|
+
return result;
|
|
568
|
+
}
|
|
569
|
+
emitProgressDone(params) {
|
|
570
|
+
params.steps.push({
|
|
571
|
+
stage: 'done',
|
|
572
|
+
status: 'success',
|
|
573
|
+
elapsedTime: Date.now() - params.startTime
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
exports.FunctionDeployer = FunctionDeployer;
|
|
578
|
+
__exportStar(require("./types"), exports);
|
|
579
|
+
var config_guard_2 = require("./function/config-guard");
|
|
580
|
+
Object.defineProperty(exports, "assertDeployConfig", { enumerable: true, get: function () { return config_guard_2.assertDeployConfig; } });
|
|
581
|
+
Object.defineProperty(exports, "checkDeployConfig", { enumerable: true, get: function () { return config_guard_2.checkDeployConfig; } });
|
|
582
|
+
Object.defineProperty(exports, "isCustomImageRuntime", { enumerable: true, get: function () { return config_guard_2.isCustomImageRuntime; } });
|
|
583
|
+
Object.defineProperty(exports, "normalizeDeployConfig", { enumerable: true, get: function () { return config_guard_2.normalizeDeployConfig; } });
|
|
584
|
+
Object.defineProperty(exports, "normalizeGatewayPath", { enumerable: true, get: function () { return config_guard_2.normalizeGatewayPath; } });
|
|
585
|
+
Object.defineProperty(exports, "parseImageReference", { enumerable: true, get: function () { return config_guard_2.parseImageReference; } });
|
|
586
|
+
var planner_2 = require("./function/planner");
|
|
587
|
+
Object.defineProperty(exports, "createDeployPlan", { enumerable: true, get: function () { return planner_2.createDeployPlan; } });
|
|
588
|
+
Object.defineProperty(exports, "toCurrentState", { enumerable: true, get: function () { return planner_2.toCurrentState; } });
|
|
589
|
+
var artifact_2 = require("./function/artifact");
|
|
590
|
+
Object.defineProperty(exports, "createCodeArtifact", { enumerable: true, get: function () { return artifact_2.createCodeArtifact; } });
|
|
591
|
+
Object.defineProperty(exports, "createImageArtifactFromConfig", { enumerable: true, get: function () { return artifact_2.createImageArtifactFromConfig; } });
|
|
592
|
+
Object.defineProperty(exports, "toCloudFunction", { enumerable: true, get: function () { return artifact_2.toCloudFunction; } });
|
|
593
|
+
var cloud_2 = require("./function/builders/cloud");
|
|
594
|
+
Object.defineProperty(exports, "buildImageOnCloud", { enumerable: true, get: function () { return cloud_2.buildImageOnCloud; } });
|
|
595
|
+
var cloud_build_service_2 = require("./function/cloud-build-service");
|
|
596
|
+
Object.defineProperty(exports, "createCloudBuildService", { enumerable: true, get: function () { return cloud_build_service_2.createCloudBuildService; } });
|
|
597
|
+
var preflight_2 = require("./function/preflight");
|
|
598
|
+
Object.defineProperty(exports, "runLocalPreflight", { enumerable: true, get: function () { return preflight_2.runLocalPreflight; } });
|
|
599
|
+
var image_preflight_2 = require("./function/image-preflight");
|
|
600
|
+
Object.defineProperty(exports, "runImageRemotePreflight", { enumerable: true, get: function () { return image_preflight_2.runImageRemotePreflight; } });
|
|
601
|
+
var cam_preflight_2 = require("./function/cam-preflight");
|
|
602
|
+
Object.defineProperty(exports, "runImagePullAuthPreflight", { enumerable: true, get: function () { return cam_preflight_2.runImagePullAuthPreflight; } });
|
|
603
|
+
Object.defineProperty(exports, "isCamPermissionError", { enumerable: true, get: function () { return cam_preflight_2.isCamPermissionError; } });
|
|
604
|
+
Object.defineProperty(exports, "SCF_PULL_IMAGE_ROLE", { enumerable: true, get: function () { return cam_preflight_2.SCF_PULL_IMAGE_ROLE; } });
|
|
605
|
+
Object.defineProperty(exports, "REQUIRED_IMAGE_PULL_POLICY", { enumerable: true, get: function () { return cam_preflight_2.REQUIRED_IMAGE_PULL_POLICY; } });
|
|
606
|
+
Object.defineProperty(exports, "IMAGE_PULL_GRANT_URL", { enumerable: true, get: function () { return cam_preflight_2.IMAGE_PULL_GRANT_URL; } });
|