@cloudbase/manager-node 5.8.2 → 5.8.4
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/CHANGELOG.md +15 -0
- package/lib/database/index.js +11 -6
- package/lib/deploy/DeployOrchestrator.js +170 -278
- package/lib/deploy/FunctionDeployer.js +310 -52
- package/lib/deploy/StaticDeployer.js +7 -1
- package/lib/deploy/function/artifact.js +55 -41
- package/lib/deploy/function/builders/cloud.js +58 -359
- package/lib/deploy/function/cam-preflight.js +2 -1
- package/lib/deploy/function/cloud-build-service.js +2 -16
- package/lib/deploy/function/config-guard.js +210 -55
- package/lib/deploy/function/enterprise-tcr.js +195 -0
- package/lib/deploy/function/image-tag.js +31 -0
- package/lib/deploy/function/local-builder.js +178 -32
- package/lib/deploy/function/personal-tcr.js +153 -0
- package/lib/deploy/function/preflight.js +3 -2
- package/lib/deploy/types.js +11 -0
- package/lib/hosting/index.js +27 -0
- package/lib/projectValidator/index.js +22 -23
- package/package.json +2 -2
- package/types/database/index.d.ts +1 -0
- package/types/deploy/DeployOrchestrator.d.ts +19 -25
- package/types/deploy/FunctionDeployer.d.ts +35 -3
- package/types/deploy/StaticDeployer.d.ts +11 -0
- package/types/deploy/function/artifact.d.ts +0 -5
- package/types/deploy/function/builders/cloud.d.ts +6 -39
- package/types/deploy/function/config-guard.d.ts +18 -0
- package/types/deploy/function/enterprise-tcr.d.ts +32 -0
- package/types/deploy/function/image-tag.d.ts +10 -0
- package/types/deploy/function/local-builder.d.ts +35 -6
- package/types/deploy/function/personal-tcr.d.ts +35 -0
- package/types/deploy/types.d.ts +110 -28
- package/types/hosting/index.d.ts +13 -0
- package/types/projectValidator/index.d.ts +3 -2
- package/types/projectValidator/types.d.ts +0 -1
- package/lib/deploy/StateStore.js +0 -328
- package/types/deploy/StateStore.d.ts +0 -174
|
@@ -6,6 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.parseImageReference = parseImageReference;
|
|
7
7
|
exports.normalizeGatewayPath = normalizeGatewayPath;
|
|
8
8
|
exports.isCustomImageRuntime = isCustomImageRuntime;
|
|
9
|
+
exports.applyMinimalDeployConfigDefaults = applyMinimalDeployConfigDefaults;
|
|
10
|
+
exports.getMissingDeployInputs = getMissingDeployInputs;
|
|
11
|
+
exports.prepareMinimalDeployConfig = prepareMinimalDeployConfig;
|
|
9
12
|
exports.normalizeDeployConfig = normalizeDeployConfig;
|
|
10
13
|
exports.checkDeployConfig = checkDeployConfig;
|
|
11
14
|
exports.assertDeployConfig = assertDeployConfig;
|
|
@@ -29,7 +32,7 @@ const IMAGE_TAG_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$/;
|
|
|
29
32
|
/** 镜像 digest 规则 */
|
|
30
33
|
const IMAGE_DIGEST_PATTERN = /^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-fA-F0-9]{32,}$/;
|
|
31
34
|
/** 镜像仓库路径规则 */
|
|
32
|
-
const IMAGE_REPOSITORY_PATTERN = /^[a-z0-9]
|
|
35
|
+
const IMAGE_REPOSITORY_PATTERN = /^[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9][a-z0-9._-]*)*$/;
|
|
33
36
|
/** 网关路径字符白名单,避免注入与歧义字符 */
|
|
34
37
|
const GATEWAY_PATH_PATTERN = /^[A-Za-z0-9\-._~%/:@]+$/;
|
|
35
38
|
/** 环境变量与构建参数名规则 */
|
|
@@ -163,18 +166,151 @@ function inferHttpBuildStrategy(config) {
|
|
|
163
166
|
if (config === null || config === void 0 ? void 0 : config.buildStrategy) {
|
|
164
167
|
return config.buildStrategy;
|
|
165
168
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
if (config === null || config === void 0 ? void 0 : config.build) {
|
|
169
|
+
const imageConfig = config === null || config === void 0 ? void 0 : config.imageConfig;
|
|
170
|
+
if (imageConfig === null || imageConfig === void 0 ? void 0 : imageConfig.build) {
|
|
170
171
|
// 有构建上下文但未声明策略时,默认使用无需本地 Docker 的云端构建
|
|
171
172
|
return 'cloud';
|
|
172
173
|
}
|
|
174
|
+
if (imageConfig === null || imageConfig === void 0 ? void 0 : imageConfig.imageUri) {
|
|
175
|
+
return 'image';
|
|
176
|
+
}
|
|
173
177
|
if (isCustomImageRuntime(config === null || config === void 0 ? void 0 : config.runtime)) {
|
|
174
178
|
return 'image';
|
|
175
179
|
}
|
|
176
180
|
return 'zip';
|
|
177
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* 应用最小化镜像配置的可安全默认值,不执行 IO,不生成凭证。
|
|
184
|
+
* 仅填充构建上下文、仓库名、namespace 和运行时固定值;用户凭证必须显式传入。
|
|
185
|
+
*
|
|
186
|
+
* 直接在 cloudbaserc 的嵌套形状上操作:镜像字段位于 imageConfig,构建目标位于
|
|
187
|
+
* imageConfig.build,不再做字段平铺映射。
|
|
188
|
+
*/
|
|
189
|
+
function applyMinimalDeployConfigDefaults(config, context = {}) {
|
|
190
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
191
|
+
const result = lodash_1.default.cloneDeep(config);
|
|
192
|
+
if ((result === null || result === void 0 ? void 0 : result.type) !== 'HTTP') {
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
const strategy = inferHttpBuildStrategy(result);
|
|
196
|
+
if (!['image', 'local', 'cloud'].includes(strategy)) {
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
result.buildStrategy = strategy;
|
|
200
|
+
const imageConfig = Object.assign({}, (result.imageConfig || {}));
|
|
201
|
+
result.imageConfig = imageConfig;
|
|
202
|
+
if (strategy === 'image') {
|
|
203
|
+
if (imageConfig.imageUri === undefined && ((_a = context.defaults) === null || _a === void 0 ? void 0 : _a.imageUri) !== undefined) {
|
|
204
|
+
imageConfig.imageUri = context.defaults.imageUri;
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
if (result.runtime === undefined) {
|
|
209
|
+
result.runtime = types_1.CUSTOM_IMAGE_RUNTIME;
|
|
210
|
+
}
|
|
211
|
+
if (imageConfig.imagePort === undefined) {
|
|
212
|
+
imageConfig.imagePort = types_1.IMAGE_FUNCTION_PORT;
|
|
213
|
+
}
|
|
214
|
+
const build = Object.assign({}, (imageConfig.build || {}));
|
|
215
|
+
imageConfig.build = build;
|
|
216
|
+
const functionPath = context.functionPath ||
|
|
217
|
+
(context.functionRootPath && result.name ? `${context.functionRootPath}/${result.name}` : undefined);
|
|
218
|
+
if (build.cwd === undefined && functionPath) {
|
|
219
|
+
build.cwd = functionPath;
|
|
220
|
+
}
|
|
221
|
+
if (build.dockerfile === undefined) {
|
|
222
|
+
build.dockerfile = types_1.DEFAULT_DOCKERFILE;
|
|
223
|
+
}
|
|
224
|
+
if (build.platform === undefined) {
|
|
225
|
+
build.platform = types_1.DEFAULT_IMAGE_PLATFORM;
|
|
226
|
+
}
|
|
227
|
+
const defaultRepository = (_c = (_b = context.defaults) === null || _b === void 0 ? void 0 : _b.repository) !== null && _c !== void 0 ? _c : result.name;
|
|
228
|
+
const defaultNamespace = (_e = (_d = context.defaults) === null || _d === void 0 ? void 0 : _d.namespace) !== null && _e !== void 0 ? _e : context.envId;
|
|
229
|
+
if (build.repository === undefined && defaultRepository !== undefined) {
|
|
230
|
+
build.repository = defaultRepository;
|
|
231
|
+
}
|
|
232
|
+
if (build.namespace === undefined && defaultNamespace !== undefined) {
|
|
233
|
+
build.namespace = defaultNamespace;
|
|
234
|
+
}
|
|
235
|
+
if (imageConfig.registryId === undefined && ((_f = context.defaults) === null || _f === void 0 ? void 0 : _f.registryId) !== undefined) {
|
|
236
|
+
imageConfig.registryId = context.defaults.registryId;
|
|
237
|
+
}
|
|
238
|
+
if (build.registryCredential === undefined && ((_g = context.defaults) === null || _g === void 0 ? void 0 : _g.registryCredential) !== undefined) {
|
|
239
|
+
build.registryCredential = lodash_1.default.cloneDeep(context.defaults.registryCredential);
|
|
240
|
+
}
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* 返回最小化部署在当前配置下仍缺失的输入字段;不执行网络、文件或凭证操作。
|
|
245
|
+
*/
|
|
246
|
+
function getMissingDeployInputs(config, context = {}) {
|
|
247
|
+
var _a, _b, _c;
|
|
248
|
+
const normalized = lodash_1.default.cloneDeep(config);
|
|
249
|
+
const missing = [];
|
|
250
|
+
if ((normalized === null || normalized === void 0 ? void 0 : normalized.type) !== 'HTTP') {
|
|
251
|
+
return missing;
|
|
252
|
+
}
|
|
253
|
+
const strategy = (_a = normalized.buildStrategy) !== null && _a !== void 0 ? _a : inferHttpBuildStrategy(normalized);
|
|
254
|
+
if (!['image', 'local', 'cloud'].includes(strategy)) {
|
|
255
|
+
return missing;
|
|
256
|
+
}
|
|
257
|
+
const imageConfig = normalized.imageConfig || {};
|
|
258
|
+
if (strategy === 'image') {
|
|
259
|
+
if (!imageConfig.imageUri) {
|
|
260
|
+
missing.push({ name: 'imageUri', path: 'imageConfig.imageUri', required: true, reason: '已有镜像部署需要固定 tag 或 digest 的 imageUri' });
|
|
261
|
+
}
|
|
262
|
+
return missing;
|
|
263
|
+
}
|
|
264
|
+
const build = imageConfig.build || {};
|
|
265
|
+
const imageType = imageConfig.imageType || (imageConfig.registryId || build.registryId ? 'enterprise' : 'personal');
|
|
266
|
+
if (imageType === 'enterprise') {
|
|
267
|
+
if (!imageConfig.registryId && !build.registryId) {
|
|
268
|
+
missing.push({ name: 'registryId', path: 'imageConfig.registryId', required: true, reason: '企业版镜像构建需要指定 TCR 实例 ID' });
|
|
269
|
+
}
|
|
270
|
+
if (!build.repository) {
|
|
271
|
+
missing.push({ name: 'repository', path: 'imageConfig.build.repository', required: true, reason: '企业版镜像构建需要目标仓库' });
|
|
272
|
+
}
|
|
273
|
+
return missing;
|
|
274
|
+
}
|
|
275
|
+
const namespace = build.namespace || context.envId;
|
|
276
|
+
if (!namespace) {
|
|
277
|
+
missing.push({ name: 'namespace', path: 'imageConfig.build.namespace', required: true, reason: '个人版镜像构建需要 namespace,且无法从 envId 推导' });
|
|
278
|
+
}
|
|
279
|
+
if (!((_b = build.registryCredential) === null || _b === void 0 ? void 0 : _b.username)) {
|
|
280
|
+
missing.push({ name: 'registryCredential.username', path: 'imageConfig.build.registryCredential.username', required: true, reason: '个人版 TCR 构建需要推送用户名' });
|
|
281
|
+
}
|
|
282
|
+
if (!((_c = build.registryCredential) === null || _c === void 0 ? void 0 : _c.password)) {
|
|
283
|
+
missing.push({ name: 'registryCredential.password', path: 'imageConfig.build.registryCredential.password', required: true, reason: '个人版 TCR 构建需要推送密码' });
|
|
284
|
+
}
|
|
285
|
+
return missing;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* 生成最小化配置准备结果:先应用安全默认值,再返回剩余缺失字段和默认值路径。
|
|
289
|
+
*/
|
|
290
|
+
function prepareMinimalDeployConfig(config, context = {}) {
|
|
291
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3;
|
|
292
|
+
const prepared = applyMinimalDeployConfigDefaults(config, context);
|
|
293
|
+
const missing = getMissingDeployInputs(prepared, { envId: context.envId });
|
|
294
|
+
const original = lodash_1.default.cloneDeep(config);
|
|
295
|
+
const preparedAny = prepared;
|
|
296
|
+
const appliedDefaults = [];
|
|
297
|
+
const candidates = [
|
|
298
|
+
['imageConfig.imageUri', (_a = original.imageConfig) === null || _a === void 0 ? void 0 : _a.imageUri, (_b = preparedAny.imageConfig) === null || _b === void 0 ? void 0 : _b.imageUri],
|
|
299
|
+
['imageConfig.build.cwd', (_d = (_c = original.imageConfig) === null || _c === void 0 ? void 0 : _c.build) === null || _d === void 0 ? void 0 : _d.cwd, (_f = (_e = preparedAny.imageConfig) === null || _e === void 0 ? void 0 : _e.build) === null || _f === void 0 ? void 0 : _f.cwd],
|
|
300
|
+
['imageConfig.build.dockerfile', (_h = (_g = original.imageConfig) === null || _g === void 0 ? void 0 : _g.build) === null || _h === void 0 ? void 0 : _h.dockerfile, (_k = (_j = preparedAny.imageConfig) === null || _j === void 0 ? void 0 : _j.build) === null || _k === void 0 ? void 0 : _k.dockerfile],
|
|
301
|
+
['imageConfig.build.platform', (_m = (_l = original.imageConfig) === null || _l === void 0 ? void 0 : _l.build) === null || _m === void 0 ? void 0 : _m.platform, (_p = (_o = preparedAny.imageConfig) === null || _o === void 0 ? void 0 : _o.build) === null || _p === void 0 ? void 0 : _p.platform],
|
|
302
|
+
['imageConfig.build.repository', (_r = (_q = original.imageConfig) === null || _q === void 0 ? void 0 : _q.build) === null || _r === void 0 ? void 0 : _r.repository, (_t = (_s = preparedAny.imageConfig) === null || _s === void 0 ? void 0 : _s.build) === null || _t === void 0 ? void 0 : _t.repository],
|
|
303
|
+
['imageConfig.build.namespace', (_v = (_u = original.imageConfig) === null || _u === void 0 ? void 0 : _u.build) === null || _v === void 0 ? void 0 : _v.namespace, (_x = (_w = preparedAny.imageConfig) === null || _w === void 0 ? void 0 : _w.build) === null || _x === void 0 ? void 0 : _x.namespace],
|
|
304
|
+
['imageConfig.registryId', (_y = original.imageConfig) === null || _y === void 0 ? void 0 : _y.registryId, (_z = preparedAny.imageConfig) === null || _z === void 0 ? void 0 : _z.registryId],
|
|
305
|
+
['imageConfig.build.registryCredential', (_1 = (_0 = original.imageConfig) === null || _0 === void 0 ? void 0 : _0.build) === null || _1 === void 0 ? void 0 : _1.registryCredential, (_3 = (_2 = preparedAny.imageConfig) === null || _2 === void 0 ? void 0 : _2.build) === null || _3 === void 0 ? void 0 : _3.registryCredential]
|
|
306
|
+
];
|
|
307
|
+
candidates.forEach(([fieldPath, before, after]) => {
|
|
308
|
+
if (before === undefined && after !== undefined) {
|
|
309
|
+
appliedDefaults.push(fieldPath);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
return { config: prepared, missing, appliedDefaults };
|
|
313
|
+
}
|
|
178
314
|
/**
|
|
179
315
|
* 规范化部署配置
|
|
180
316
|
*
|
|
@@ -182,7 +318,7 @@ function inferHttpBuildStrategy(config) {
|
|
|
182
318
|
* 以便 checkDeployConfig 汇总全部问题
|
|
183
319
|
*/
|
|
184
320
|
function normalizeDeployConfig(config) {
|
|
185
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j
|
|
321
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
186
322
|
if (!config || typeof config !== 'object') {
|
|
187
323
|
throw new error_1.CloudBaseError('部署配置必须是对象', {
|
|
188
324
|
code: types_1.FUNCTION_DEPLOY_ERROR.CONFIG_INVALID
|
|
@@ -210,20 +346,20 @@ function normalizeDeployConfig(config) {
|
|
|
210
346
|
// node_modules 必须随代码包上传,不能被 ignore 排除
|
|
211
347
|
ignore: ignoreList.filter((item) => !/node_modules/.test(item)) });
|
|
212
348
|
}
|
|
213
|
-
const
|
|
214
|
-
normalizedImage.
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
349
|
+
const imageConfig = Object.assign({}, (cloned.imageConfig || {}));
|
|
350
|
+
const normalizedImage = Object.assign(Object.assign({}, cloned), { type: 'HTTP', buildStrategy, runtime: types_1.CUSTOM_IMAGE_RUNTIME, imageConfig });
|
|
351
|
+
imageConfig.imagePort = (_a = imageConfig.imagePort) !== null && _a !== void 0 ? _a : types_1.IMAGE_FUNCTION_PORT;
|
|
352
|
+
imageConfig.imageType =
|
|
353
|
+
(_b = imageConfig.imageType) !== null && _b !== void 0 ? _b : (((_c = imageConfig.registryId) !== null && _c !== void 0 ? _c : (_d = imageConfig.build) === null || _d === void 0 ? void 0 : _d.registryId) ? 'enterprise' : 'personal');
|
|
218
354
|
// 镜像函数由容器启动命令决定入口,不使用 handler 与云端装依赖
|
|
219
355
|
delete normalizedImage.handler;
|
|
220
356
|
delete normalizedImage.installDependency;
|
|
221
357
|
if (buildStrategy === 'cloud' || buildStrategy === 'local') {
|
|
222
|
-
|
|
358
|
+
imageConfig.build = Object.assign(Object.assign({}, (imageConfig.build || {})), { dockerfile: (_f = (_e = imageConfig.build) === null || _e === void 0 ? void 0 : _e.dockerfile) !== null && _f !== void 0 ? _f : types_1.DEFAULT_DOCKERFILE, platform: (_h = (_g = imageConfig.build) === null || _g === void 0 ? void 0 : _g.platform) !== null && _h !== void 0 ? _h : types_1.DEFAULT_IMAGE_PLATFORM });
|
|
223
359
|
}
|
|
224
360
|
if (buildStrategy === 'local') {
|
|
225
361
|
// 默认不静默改变构建环境
|
|
226
|
-
|
|
362
|
+
imageConfig.localFallback = (_j = imageConfig.localFallback) !== null && _j !== void 0 ? _j : 'error';
|
|
227
363
|
}
|
|
228
364
|
return normalizedImage;
|
|
229
365
|
}
|
|
@@ -334,10 +470,11 @@ function checkHttpCommon(raw, checks) {
|
|
|
334
470
|
}
|
|
335
471
|
/** 校验镜像函数公共约束 */
|
|
336
472
|
function checkImageRuntimeCommon(raw, checks) {
|
|
473
|
+
var _a;
|
|
337
474
|
if (raw.runtime !== undefined && !isCustomImageRuntime(raw.runtime)) {
|
|
338
475
|
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.IMAGE_RUNTIME_INVALID, `镜像部署要求 runtime 为 ${types_1.CUSTOM_IMAGE_RUNTIME}`, `当前为 ${raw.runtime},请移除 runtime 或设置为 ${types_1.CUSTOM_IMAGE_RUNTIME}`));
|
|
339
476
|
}
|
|
340
|
-
if (raw.imagePort !== undefined && raw.imagePort !== types_1.IMAGE_FUNCTION_PORT) {
|
|
477
|
+
if (((_a = raw.imageConfig) === null || _a === void 0 ? void 0 : _a.imagePort) !== undefined && raw.imageConfig.imagePort !== types_1.IMAGE_FUNCTION_PORT) {
|
|
341
478
|
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.IMAGE_PORT_INVALID, `镜像监听端口必须为 ${types_1.IMAGE_FUNCTION_PORT}`, `请让容器监听 ${types_1.IMAGE_FUNCTION_PORT} 端口`));
|
|
342
479
|
}
|
|
343
480
|
if (raw.handler !== undefined) {
|
|
@@ -369,40 +506,46 @@ function checkImageReference(imageConfig, checks) {
|
|
|
369
506
|
}
|
|
370
507
|
/**
|
|
371
508
|
* 校验镜像类型与仓库 ID
|
|
372
|
-
*
|
|
373
|
-
* local
|
|
509
|
+
* 直接读取 cloudbaserc 嵌套形状:imageType 位于 imageConfig.imageType;
|
|
510
|
+
* registryId 位于 imageConfig.registryId(cloud/local 兼容 imageConfig.build.registryId)。
|
|
374
511
|
*/
|
|
375
512
|
function checkImageIdentity(raw, checks) {
|
|
376
|
-
var _a
|
|
513
|
+
var _a;
|
|
377
514
|
const imageConfig = raw === null || raw === void 0 ? void 0 : raw.imageConfig;
|
|
378
|
-
const
|
|
515
|
+
const build = imageConfig === null || imageConfig === void 0 ? void 0 : imageConfig.build;
|
|
516
|
+
const buildRegistryId = build === null || build === void 0 ? void 0 : build.registryId;
|
|
379
517
|
const buildStrategy = raw === null || raw === void 0 ? void 0 : raw.buildStrategy;
|
|
380
|
-
const registryId = (
|
|
381
|
-
const
|
|
382
|
-
const imageType = (_e = (_d = raw === null || raw === void 0 ? void 0 : raw.imageType) !== null && _d !== void 0 ? _d : imageConfig === null || imageConfig === void 0 ? void 0 : imageConfig.imageType) !== null && _e !== void 0 ? _e : defaultImageType;
|
|
518
|
+
const registryId = (_a = imageConfig === null || imageConfig === void 0 ? void 0 : imageConfig.registryId) !== null && _a !== void 0 ? _a : buildRegistryId;
|
|
519
|
+
const imageType = resolveBuildImageType(raw, build !== null && build !== void 0 ? build : {});
|
|
383
520
|
if (imageType !== 'enterprise' && imageType !== 'personal') {
|
|
384
|
-
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.IMAGE_TYPE_INVALID, '
|
|
521
|
+
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.IMAGE_TYPE_INVALID, buildStrategy === 'cloud' || buildStrategy === 'local'
|
|
522
|
+
? `${buildStrategy} 策略必须显式声明镜像类型`
|
|
523
|
+
: '镜像类型不合法', '请配置 imageConfig.imageType 为 enterprise 或 personal'));
|
|
385
524
|
return;
|
|
386
525
|
}
|
|
387
526
|
if (imageType === 'enterprise') {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
: 'registryId';
|
|
391
|
-
if (registryId === undefined || registryId === null) {
|
|
392
|
-
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.IMAGE_REGISTRY_ID_MISSING, '企业版镜像未配置仓库实例 ID', `请配置 ${registryField};如果当前没有企业版 TCR 实例,请将 imageType 设置为 personal 并改用个人版仓库配置`));
|
|
527
|
+
if (buildStrategy === 'image' && (registryId === undefined || registryId === null)) {
|
|
528
|
+
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.IMAGE_REGISTRY_ID_MISSING, '企业版镜像未配置仓库实例 ID', '请配置 imageConfig.registryId'));
|
|
393
529
|
}
|
|
394
|
-
else if (typeof registryId !== 'string' || !registryId.trim()) {
|
|
395
|
-
|
|
530
|
+
else if (registryId !== undefined && (typeof registryId !== 'string' || !registryId.trim())) {
|
|
531
|
+
const registryField = buildStrategy === 'cloud' || buildStrategy === 'local'
|
|
532
|
+
? 'imageConfig.build.registryId'
|
|
533
|
+
: 'imageConfig.registryId';
|
|
534
|
+
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.IMAGE_REGISTRY_ID_INVALID, '企业版镜像仓库实例 ID 无效', `${registryField} 已声明但解析结果为空;若使用 {{env.*}},请检查对应环境变量已赋值为实际企业版 TCR 实例 ID`));
|
|
396
535
|
}
|
|
397
536
|
}
|
|
398
537
|
else if (registryId !== undefined) {
|
|
399
|
-
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.IMAGE_TYPE_INVALID, '个人版镜像不能配置企业版仓库实例 ID', '请移除 build.registryId,或将 imageType 设置为 enterprise'));
|
|
538
|
+
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.IMAGE_TYPE_INVALID, '个人版镜像不能配置企业版仓库实例 ID', '请移除 imageConfig.build.registryId,或将 imageConfig.imageType 设置为 enterprise'));
|
|
400
539
|
}
|
|
401
540
|
}
|
|
402
541
|
/** 解析构建策略使用的镜像类型 */
|
|
403
542
|
function resolveBuildImageType(raw, build) {
|
|
404
|
-
var _a, _b, _c, _d
|
|
405
|
-
return (
|
|
543
|
+
var _a, _b, _c, _d;
|
|
544
|
+
return (_b = (_a = raw.imageConfig) === null || _a === void 0 ? void 0 : _a.imageType) !== null && _b !== void 0 ? _b : (raw.buildStrategy === 'local' || raw.buildStrategy === 'cloud'
|
|
545
|
+
? 'enterprise'
|
|
546
|
+
: ((_d = (_c = raw.imageConfig) === null || _c === void 0 ? void 0 : _c.registryId) !== null && _d !== void 0 ? _d : build.registryId)
|
|
547
|
+
? 'enterprise'
|
|
548
|
+
: 'personal');
|
|
406
549
|
}
|
|
407
550
|
/** 校验构建 tag */
|
|
408
551
|
function checkBuildTag(raw, build) {
|
|
@@ -441,8 +584,9 @@ function checkBuildSource(build, checks) {
|
|
|
441
584
|
}
|
|
442
585
|
/** 判断当前构建策略是否要求完整仓库地址 */
|
|
443
586
|
function requiresFullBuildRepository(raw, imageType) {
|
|
444
|
-
|
|
445
|
-
|
|
587
|
+
var _a, _b;
|
|
588
|
+
return raw.buildStrategy === 'local' && imageType === 'personal' &&
|
|
589
|
+
((_b = (_a = raw.imageConfig) === null || _a === void 0 ? void 0 : _a.build) === null || _b === void 0 ? void 0 : _b.repository) !== undefined;
|
|
446
590
|
}
|
|
447
591
|
/** 添加仓库地址缺失或不完整的错误 */
|
|
448
592
|
function addFullRepositoryError(raw, checks, missing) {
|
|
@@ -470,14 +614,21 @@ function checkBuildRepository(raw, build, checks) {
|
|
|
470
614
|
return;
|
|
471
615
|
}
|
|
472
616
|
const repository = typeof build.repository === 'string' ? build.repository.trim() : build.repository;
|
|
473
|
-
const
|
|
474
|
-
|
|
617
|
+
const isShortRepository = typeof repository === 'string' &&
|
|
618
|
+
IMAGE_REPOSITORY_PATTERN.test(repository) &&
|
|
619
|
+
!repository.split('/')[0].includes('.') &&
|
|
620
|
+
!repository.split('/')[0].includes(':');
|
|
621
|
+
const parsedRepository = typeof repository === 'string' && !isShortRepository
|
|
622
|
+
? parseImageReference(repository)
|
|
623
|
+
: null;
|
|
624
|
+
if (!isShortRepository && (!parsedRepository || parsedRepository.tag || parsedRepository.digest)) {
|
|
475
625
|
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.BUILD_REPOSITORY_INVALID, '镜像仓库地址不合法', 'repository 需为不含 tag 或 digest 的镜像仓库路径'));
|
|
476
626
|
return;
|
|
477
627
|
}
|
|
478
|
-
const repositoryPath = parsedRepository.repository;
|
|
628
|
+
const repositoryPath = isShortRepository ? repository : parsedRepository.repository;
|
|
479
629
|
if (requiresFullRepository &&
|
|
480
|
-
(!parsedRepository.registry || !repositoryPath.includes('/'))
|
|
630
|
+
(!(parsedRepository === null || parsedRepository === void 0 ? void 0 : parsedRepository.registry) || !repositoryPath.includes('/')) &&
|
|
631
|
+
repository !== raw.name) {
|
|
481
632
|
addFullRepositoryError(raw, checks, false);
|
|
482
633
|
}
|
|
483
634
|
}
|
|
@@ -511,10 +662,10 @@ function checkBuildArgs(raw, build, checks) {
|
|
|
511
662
|
checkBuildArg(raw, buildArg, checks);
|
|
512
663
|
});
|
|
513
664
|
}
|
|
514
|
-
/** 校验个人版 TCR
|
|
665
|
+
/** 校验个人版 TCR 推送凭证 */
|
|
515
666
|
function checkPersonalRegistryCredential(registryCredential, checks) {
|
|
516
667
|
if (!lodash_1.default.isPlainObject(registryCredential)) {
|
|
517
|
-
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.CLOUD_REGISTRY_CREDENTIAL_MISSING, '个人版 TCR
|
|
668
|
+
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.CLOUD_REGISTRY_CREDENTIAL_MISSING, '个人版 TCR 构建缺少推送凭证', '请在 build.registryCredential 中显式声明 username/password,' +
|
|
518
669
|
'推荐使用 {{env.TCB_TCR_USERNAME}} 与 {{env.TCB_TCR_PASSWORD}}'));
|
|
519
670
|
return;
|
|
520
671
|
}
|
|
@@ -527,27 +678,30 @@ function checkPersonalRegistryCredential(registryCredential, checks) {
|
|
|
527
678
|
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.CLOUD_REGISTRY_CREDENTIAL_INVALID, '个人版 TCR 固定密码未配置或长度不合法', 'password 必须显式声明且不能为空,推荐使用 {{env.TCB_TCR_PASSWORD}}'));
|
|
528
679
|
}
|
|
529
680
|
}
|
|
530
|
-
/**
|
|
681
|
+
/** 校验镜像仓库配置 */
|
|
531
682
|
function checkBuildRegistry(raw, build, checks) {
|
|
532
683
|
const imageType = resolveBuildImageType(raw, build);
|
|
533
684
|
const registryCredential = build.registryCredential;
|
|
534
|
-
if (
|
|
535
|
-
if (
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
685
|
+
if (imageType === 'personal') {
|
|
686
|
+
if (build.namespace !== undefined &&
|
|
687
|
+
(typeof build.namespace !== 'string' ||
|
|
688
|
+
!build.namespace.trim() ||
|
|
689
|
+
!IMAGE_REPOSITORY_PATTERN.test(build.namespace.trim()))) {
|
|
690
|
+
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.BUILD_REPOSITORY_INVALID, '个人版 TCR 命名空间未配置或不合法', 'build.namespace 必须为合法的个人版 TCR 命名空间;未声明时可由 envId 默认补齐'));
|
|
539
691
|
}
|
|
540
692
|
checkPersonalRegistryCredential(registryCredential, checks);
|
|
693
|
+
return;
|
|
541
694
|
}
|
|
542
|
-
|
|
543
|
-
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.CLOUD_REGISTRY_CREDENTIAL_INVALID, 'registryCredential 仅支持
|
|
695
|
+
if (registryCredential !== undefined) {
|
|
696
|
+
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.CLOUD_REGISTRY_CREDENTIAL_INVALID, 'registryCredential 仅支持 personal 镜像构建', '企业版使用 STS 临时凭证换取 token;personal local/cloud 必须显式提供 username/password'));
|
|
544
697
|
}
|
|
545
698
|
}
|
|
546
699
|
/** 校验镜像构建目标配置 */
|
|
547
700
|
function checkBuildTarget(raw, checks) {
|
|
548
|
-
|
|
701
|
+
var _a, _b;
|
|
702
|
+
const build = (_a = raw.imageConfig) === null || _a === void 0 ? void 0 : _a.build;
|
|
549
703
|
if (!lodash_1.default.isPlainObject(build)) {
|
|
550
|
-
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.BUILD_CONFIG_MISSING, '缺少镜像构建配置', '请配置 build,至少包含构建上下文与 Dockerfile 信息'));
|
|
704
|
+
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.BUILD_CONFIG_MISSING, '缺少镜像构建配置', '请配置 imageConfig.build,至少包含构建上下文与 Dockerfile 信息'));
|
|
551
705
|
return;
|
|
552
706
|
}
|
|
553
707
|
checks.push(...checkBuildTag(raw, build));
|
|
@@ -555,8 +709,8 @@ function checkBuildTarget(raw, checks) {
|
|
|
555
709
|
checkBuildRepository(raw, build, checks);
|
|
556
710
|
checkBuildArgs(raw, build, checks);
|
|
557
711
|
checkBuildRegistry(raw, build, checks);
|
|
558
|
-
if (raw.imageConfig !== undefined) {
|
|
559
|
-
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.IMAGE_CONFIG_NOT_ALLOWED, `buildStrategy 为 ${raw.buildStrategy} 时不能指定 imageConfig`, '镜像地址将由构建结果决定,如需直接部署已有镜像请使用 buildStrategy: image'));
|
|
712
|
+
if (((_b = raw.imageConfig) === null || _b === void 0 ? void 0 : _b.imageUri) !== undefined) {
|
|
713
|
+
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.IMAGE_CONFIG_NOT_ALLOWED, `buildStrategy 为 ${raw.buildStrategy} 时不能指定 imageConfig.imageUri`, '镜像地址将由构建结果决定,如需直接部署已有镜像请使用 buildStrategy: image'));
|
|
560
714
|
}
|
|
561
715
|
}
|
|
562
716
|
/**
|
|
@@ -564,6 +718,7 @@ function checkBuildTarget(raw, checks) {
|
|
|
564
718
|
* @returns 检查报告,包含全部问题而非仅第一个
|
|
565
719
|
*/
|
|
566
720
|
function checkDeployConfig(config) {
|
|
721
|
+
var _a;
|
|
567
722
|
const checks = [];
|
|
568
723
|
if (!config || typeof config !== 'object') {
|
|
569
724
|
return {
|
|
@@ -613,16 +768,16 @@ function checkDeployConfig(config) {
|
|
|
613
768
|
}
|
|
614
769
|
else {
|
|
615
770
|
checkImageReference(raw.imageConfig, checks);
|
|
616
|
-
// imageType
|
|
771
|
+
// imageType 与 registryId 位于 imageConfig,统一传 raw 由 checkImageIdentity 读取
|
|
617
772
|
checkImageIdentity(raw, checks);
|
|
618
|
-
// 注:imagePort
|
|
773
|
+
// 注:imagePort 位于 imageConfig,已由 checkImageRuntimeCommon 校验,此处不重复
|
|
619
774
|
}
|
|
620
775
|
}
|
|
621
776
|
else {
|
|
622
777
|
checkImageIdentity(raw, checks);
|
|
623
778
|
checkBuildTarget(raw, checks);
|
|
624
779
|
if (strategy === 'local') {
|
|
625
|
-
const fallback = raw.localFallback;
|
|
780
|
+
const fallback = (_a = raw.imageConfig) === null || _a === void 0 ? void 0 : _a.localFallback;
|
|
626
781
|
if (fallback !== undefined && fallback !== 'cloud' && fallback !== 'error') {
|
|
627
782
|
checks.push(fail(types_1.FUNCTION_DEPLOY_ERROR.LOCAL_FALLBACK_INVALID, 'localFallback 取值不合法', "只能是 'cloud' 或 'error'"));
|
|
628
783
|
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.prepareEnterpriseTcrTarget = prepareEnterpriseTcrTarget;
|
|
4
|
+
exports.createEnterpriseTcrCredential = createEnterpriseTcrCredential;
|
|
5
|
+
exports.createTcrApiService = createTcrApiService;
|
|
6
|
+
const error_1 = require("../../error");
|
|
7
|
+
const types_1 = require("../types");
|
|
8
|
+
const config_guard_1 = require("./config-guard");
|
|
9
|
+
const TCR_API_VERSION = '2019-09-24';
|
|
10
|
+
const NAMESPACE_PATTERN = /^[a-z0-9](?:[a-z0-9]|[._-](?![._-])){0,28}[a-z0-9]$/;
|
|
11
|
+
function responseOf(result) {
|
|
12
|
+
return (result === null || result === void 0 ? void 0 : result.Response) || result || {};
|
|
13
|
+
}
|
|
14
|
+
function requestIdOf(result) {
|
|
15
|
+
var _a;
|
|
16
|
+
const requestId = (_a = responseOf(result)) === null || _a === void 0 ? void 0 : _a.RequestId;
|
|
17
|
+
return typeof requestId === 'string' && requestId ? requestId : undefined;
|
|
18
|
+
}
|
|
19
|
+
function errorRequestId(error) {
|
|
20
|
+
var _a, _b, _c;
|
|
21
|
+
return (error === null || error === void 0 ? void 0 : error.requestId) || ((_a = error === null || error === void 0 ? void 0 : error.original) === null || _a === void 0 ? void 0 : _a.requestId) || ((_c = (_b = error === null || error === void 0 ? void 0 : error.original) === null || _b === void 0 ? void 0 : _b.Response) === null || _c === void 0 ? void 0 : _c.RequestId);
|
|
22
|
+
}
|
|
23
|
+
function throwTcrError(message, code, requestId) {
|
|
24
|
+
throw new error_1.CloudBaseError(message, Object.assign({ code }, (requestId ? { requestId } : {})));
|
|
25
|
+
}
|
|
26
|
+
function assertNamespace(namespace) {
|
|
27
|
+
if (!NAMESPACE_PATTERN.test(namespace)) {
|
|
28
|
+
throwTcrError(`企业版 TCR 命名空间不合法:${namespace}。名称需为 2-30 位小写字母、数字、点、下划线或中划线,且不能以分隔符开头、结尾或连续出现。`, types_1.FUNCTION_DEPLOY_ERROR.TCR_NAMESPACE_INVALID);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function parseRepositoryTarget(repository) {
|
|
32
|
+
const parsed = (0, config_guard_1.parseImageReference)(repository);
|
|
33
|
+
if (!parsed || parsed.tag || parsed.digest) {
|
|
34
|
+
throwTcrError('企业版 TCR repository 不合法,需为 repository、namespace/repository 或 registry/namespace/repository,且不能包含 tag 或 digest。', types_1.FUNCTION_DEPLOY_ERROR.BUILD_REPOSITORY_INVALID);
|
|
35
|
+
}
|
|
36
|
+
const parts = parsed.repository.split('/');
|
|
37
|
+
if (parsed.registry) {
|
|
38
|
+
if (parts.length < 2) {
|
|
39
|
+
throwTcrError('完整企业版 TCR repository 必须包含 namespace/repository。', types_1.FUNCTION_DEPLOY_ERROR.BUILD_REPOSITORY_INVALID);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
registry: parsed.registry,
|
|
43
|
+
namespace: parts.slice(0, -1).join('/'),
|
|
44
|
+
repository: parts[parts.length - 1]
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (parts.length >= 2) {
|
|
48
|
+
return {
|
|
49
|
+
namespace: parts.slice(0, -1).join('/'),
|
|
50
|
+
repository: parts[parts.length - 1]
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return { repository: parts[0] };
|
|
54
|
+
}
|
|
55
|
+
function isRunningRegistry(registry) {
|
|
56
|
+
return !registry.Status || registry.Status.toLowerCase() === 'running';
|
|
57
|
+
}
|
|
58
|
+
async function resolveRegistry(service, region, explicitRegistryId) {
|
|
59
|
+
let result;
|
|
60
|
+
try {
|
|
61
|
+
result = await service.call({
|
|
62
|
+
Action: 'DescribeInstances',
|
|
63
|
+
Param: Object.assign({ AllRegion: true, Limit: 100 }, (explicitRegistryId ? { Registryids: [explicitRegistryId] } : {}))
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw new error_1.CloudBaseError('查询企业版 TCR 实例失败,请确认当前身份具备 DescribeInstances 权限。', {
|
|
68
|
+
code: types_1.FUNCTION_DEPLOY_ERROR.TCR_INSTANCE_DISCOVERY_FAILED,
|
|
69
|
+
requestId: errorRequestId(error) || ''
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const response = responseOf(result);
|
|
73
|
+
const registries = Array.isArray(response.Registries)
|
|
74
|
+
? response.Registries
|
|
75
|
+
: Array.isArray(response.InstanceSet)
|
|
76
|
+
? response.InstanceSet
|
|
77
|
+
: [];
|
|
78
|
+
const matching = registries.filter(item => {
|
|
79
|
+
const registryId = item.RegistryId || item.InstanceId;
|
|
80
|
+
const registryRegion = item.RegionName || item.Region;
|
|
81
|
+
return ((!explicitRegistryId || registryId === explicitRegistryId) &&
|
|
82
|
+
registryRegion === region &&
|
|
83
|
+
isRunningRegistry(item));
|
|
84
|
+
});
|
|
85
|
+
const requestId = requestIdOf(result);
|
|
86
|
+
if (matching.length === 0) {
|
|
87
|
+
const message = explicitRegistryId
|
|
88
|
+
? `未找到地域 ${region} 内可用的企业版 TCR 实例 ${explicitRegistryId},请确认实例 ID、状态及其与 SCF 的地域一致。`
|
|
89
|
+
: `地域 ${region} 内没有可用的企业版 TCR 实例,请先创建实例或显式指定同地域 registryId。`;
|
|
90
|
+
throwTcrError(message, types_1.FUNCTION_DEPLOY_ERROR.TCR_INSTANCE_NOT_FOUND, requestId);
|
|
91
|
+
}
|
|
92
|
+
if (matching.length > 1) {
|
|
93
|
+
throwTcrError(`地域 ${region} 内发现多个可用的企业版 TCR 实例,请通过 imageConfig.registryId 显式指定目标实例。`, types_1.FUNCTION_DEPLOY_ERROR.TCR_INSTANCE_AMBIGUOUS, requestId);
|
|
94
|
+
}
|
|
95
|
+
const selected = matching[0];
|
|
96
|
+
const registryId = selected.RegistryId || selected.InstanceId;
|
|
97
|
+
const registry = selected.PublicDomain || selected.RegistryDomain || selected.RegistryDomainName;
|
|
98
|
+
if (!registryId || !registry) {
|
|
99
|
+
throwTcrError('企业版 TCR 实例信息缺少 RegistryId 或 PublicDomain,无法生成镜像地址。', types_1.FUNCTION_DEPLOY_ERROR.TCR_INSTANCE_INVALID, requestId);
|
|
100
|
+
}
|
|
101
|
+
return { registryId, registry };
|
|
102
|
+
}
|
|
103
|
+
function isNamespaceConflict(error) {
|
|
104
|
+
var _a, _b;
|
|
105
|
+
const code = String((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) || '');
|
|
106
|
+
return /ResourceConflict|AlreadyExists|ResourceInUse/i.test(code);
|
|
107
|
+
}
|
|
108
|
+
async function ensureNamespace(service, registryId, namespace) {
|
|
109
|
+
var _a;
|
|
110
|
+
let describeResult;
|
|
111
|
+
try {
|
|
112
|
+
describeResult = await service.call({
|
|
113
|
+
Action: 'DescribeNamespaces',
|
|
114
|
+
Param: { RegistryId: registryId, NamespaceName: namespace, Limit: 1, Offset: 0 }
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
throw new error_1.CloudBaseError(`查询企业版 TCR 命名空间 ${namespace} 失败。`, {
|
|
119
|
+
code: types_1.FUNCTION_DEPLOY_ERROR.TCR_NAMESPACE_QUERY_FAILED,
|
|
120
|
+
requestId: errorRequestId(error) || ''
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
const namespaceList = (_a = responseOf(describeResult)) === null || _a === void 0 ? void 0 : _a.NamespaceList;
|
|
124
|
+
if (Array.isArray(namespaceList) && namespaceList.some(item => (item === null || item === void 0 ? void 0 : item.Name) === namespace)) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
await service.call({
|
|
129
|
+
Action: 'CreateNamespace',
|
|
130
|
+
Param: { RegistryId: registryId, NamespaceName: namespace, IsPublic: false }
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
if (isNamespaceConflict(error)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
throw new error_1.CloudBaseError(`创建企业版 TCR 命名空间 ${namespace} 失败。`, {
|
|
138
|
+
code: types_1.FUNCTION_DEPLOY_ERROR.TCR_NAMESPACE_CREATE_FAILED,
|
|
139
|
+
requestId: errorRequestId(error) || ''
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async function prepareEnterpriseTcrTarget(config, options) {
|
|
144
|
+
var _a;
|
|
145
|
+
const build = config.imageConfig.build;
|
|
146
|
+
const configuredRegistryId = (_a = config.imageConfig.registryId) !== null && _a !== void 0 ? _a : build.registryId;
|
|
147
|
+
const explicitRegistryId = typeof configuredRegistryId === 'string' && configuredRegistryId.trim()
|
|
148
|
+
? configuredRegistryId.trim()
|
|
149
|
+
: undefined;
|
|
150
|
+
const resolvedRegistry = await resolveRegistry(options.service, options.region, explicitRegistryId);
|
|
151
|
+
const repositoryTarget = parseRepositoryTarget(build.repository || config.name);
|
|
152
|
+
const namespace = (build.namespace || repositoryTarget.namespace || options.envId).trim();
|
|
153
|
+
assertNamespace(namespace);
|
|
154
|
+
if (build.namespace && repositoryTarget.namespace && build.namespace !== repositoryTarget.namespace) {
|
|
155
|
+
throwTcrError('imageConfig.build.namespace 与 imageConfig.build.repository 中的命名空间不一致。', types_1.FUNCTION_DEPLOY_ERROR.BUILD_REPOSITORY_INVALID);
|
|
156
|
+
}
|
|
157
|
+
await ensureNamespace(options.service, resolvedRegistry.registryId, namespace);
|
|
158
|
+
const registry = repositoryTarget.registry || resolvedRegistry.registry;
|
|
159
|
+
const repository = repositoryTarget.repository || config.name;
|
|
160
|
+
const imageRepository = `${registry}/${namespace}/${repository}`;
|
|
161
|
+
const preparedConfig = Object.assign(Object.assign({}, config), { imageConfig: Object.assign(Object.assign({}, config.imageConfig), { imageType: 'enterprise', registryId: resolvedRegistry.registryId, build: Object.assign(Object.assign({}, build), { registryId: resolvedRegistry.registryId, namespace, repository: imageRepository }) }) });
|
|
162
|
+
return {
|
|
163
|
+
config: preparedConfig,
|
|
164
|
+
target: {
|
|
165
|
+
registryId: resolvedRegistry.registryId,
|
|
166
|
+
registry,
|
|
167
|
+
namespace,
|
|
168
|
+
repository,
|
|
169
|
+
imageRepository
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
async function createEnterpriseTcrCredential(service, registryId) {
|
|
174
|
+
let result;
|
|
175
|
+
try {
|
|
176
|
+
result = await service.call({
|
|
177
|
+
Action: 'CreateInstanceToken',
|
|
178
|
+
Param: { RegistryId: registryId, TokenType: 'temp' }
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
throw new error_1.CloudBaseError('获取企业版 TCR 临时登录凭证失败,请确认当前身份具备 CreateInstanceToken 权限。', {
|
|
183
|
+
code: types_1.FUNCTION_DEPLOY_ERROR.TCR_TOKEN_CREATE_FAILED,
|
|
184
|
+
requestId: errorRequestId(error) || ''
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
const response = responseOf(result);
|
|
188
|
+
if (typeof response.Username !== 'string' || !response.Username || typeof response.Token !== 'string' || !response.Token) {
|
|
189
|
+
throwTcrError('CreateInstanceToken 未返回有效的 Username 或 Token。', types_1.FUNCTION_DEPLOY_ERROR.TCR_TOKEN_INVALID, requestIdOf(result));
|
|
190
|
+
}
|
|
191
|
+
return { username: response.Username, token: response.Token };
|
|
192
|
+
}
|
|
193
|
+
function createTcrApiService(environment) {
|
|
194
|
+
return environment.getCommonService('tcr', TCR_API_VERSION);
|
|
195
|
+
}
|