@cloudbase/cli 2.0.10 → 2.0.11
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/bin/cloudbase.js +0 -0
- package/bin/tcb.js +0 -0
- package/lib/commands/run/service/create.js +4 -0
- package/lib/commands/run/service/deploy.js +4 -0
- package/lib/commands/run/service/update.js +4 -0
- package/lib/run/service/common.js +115 -21
- package/lib/types.js +6 -1
- package/package.json +1 -1
- package/src/commands/run/service/create.ts +4 -0
- package/src/commands/run/service/deploy.ts +4 -0
- package/src/commands/run/service/list.ts +4 -4
- package/src/commands/run/service/update.ts +4 -0
- package/src/run/service/common.ts +129 -24
- package/src/run/service/create.ts +1 -1
- package/src/run/service/update.ts +2 -1
- package/src/types.ts +13 -0
- package/types/run/service/common.d.ts +4 -1
- package/types/run/service/create.d.ts +1 -1
- package/types/run/service/update.d.ts +2 -1
- package/types/types.d.ts +11 -0
package/bin/cloudbase.js
CHANGED
|
File without changes
|
package/bin/tcb.js
CHANGED
|
File without changes
|
|
@@ -97,6 +97,10 @@ let CreateServiceTcbr = class CreateServiceTcbr extends common_1.Command {
|
|
|
97
97
|
flags: '--dockerfile <dockerfile>',
|
|
98
98
|
desc: 'Dockerfile文件名,默认为 Dockerfile'
|
|
99
99
|
},
|
|
100
|
+
{
|
|
101
|
+
flags: '--custom_image <custom_image>',
|
|
102
|
+
desc: 'TCR 仓库镜像 URL'
|
|
103
|
+
},
|
|
100
104
|
{
|
|
101
105
|
flags: '--image <image>',
|
|
102
106
|
desc: '镜像标签或ID'
|
|
@@ -96,6 +96,10 @@ let DeployServiceTcbr = class DeployServiceTcbr extends common_1.Command {
|
|
|
96
96
|
flags: '--dockerfile <dockerfile>',
|
|
97
97
|
desc: 'Dockerfile文件名,默认为 Dockerfile'
|
|
98
98
|
},
|
|
99
|
+
{
|
|
100
|
+
flags: '--custom_image <custom_image>',
|
|
101
|
+
desc: 'TCR 仓库镜像 URL'
|
|
102
|
+
},
|
|
99
103
|
{
|
|
100
104
|
flags: '--library_image <library_image>',
|
|
101
105
|
desc: '线上镜像仓库的 tag,仅在服务已存在时可用'
|
|
@@ -92,6 +92,10 @@ let UpdateServiceTcbr = class UpdateServiceTcbr extends common_1.Command {
|
|
|
92
92
|
flags: '--dockerfile <dockerfile>',
|
|
93
93
|
desc: 'Dockerfile文件名,默认为 Dockerfile'
|
|
94
94
|
},
|
|
95
|
+
{
|
|
96
|
+
flags: '--custom_image <custom_image>',
|
|
97
|
+
desc: 'TCR 仓库镜像 URL'
|
|
98
|
+
},
|
|
95
99
|
{
|
|
96
100
|
flags: '--library_image <library_image>',
|
|
97
101
|
desc: '线上镜像仓库的 tag'
|
|
@@ -9,13 +9,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.tcbrServiceOptions = exports.mergeEnvParams = exports.parseEnvParams = exports.extractPolicyDetails = exports.convertNumber = exports.describeWxCloudBaseRunReleaseOrder = void 0;
|
|
12
|
+
exports.validateTcrImageURL = exports.getAuthorizedTcrInstance = exports.tcbrServiceOptions = exports.mergeEnvParams = exports.parseEnvParams = exports.extractPolicyDetails = exports.convertNumber = exports.describeWxCloudBaseRunReleaseOrder = void 0;
|
|
13
13
|
const utils_1 = require("../../utils");
|
|
14
|
+
const types_1 = require("../../types");
|
|
14
15
|
const toolbox_1 = require("@cloudbase/toolbox");
|
|
15
16
|
const index_1 = require("./index");
|
|
16
17
|
const __1 = require("..");
|
|
17
18
|
const constant_1 = require("../../constant");
|
|
18
19
|
const tcbService = utils_1.CloudApiService.getInstance('tcb');
|
|
20
|
+
const tcrCloudApiService = new utils_1.CloudApiService('tcr', {}, '2019-09-24');
|
|
19
21
|
function describeWxCloudBaseRunReleaseOrder(options) {
|
|
20
22
|
return __awaiter(this, void 0, void 0, function* () {
|
|
21
23
|
const res = yield tcbService.request('DescribeWxCloudBaseRunReleaseOrder', {
|
|
@@ -76,31 +78,32 @@ function checkRequiredParams(options) {
|
|
|
76
78
|
if (!options.containerPort) {
|
|
77
79
|
throw new toolbox_1.CloudBaseError('必须使用 --containerPort 指定监听端口号');
|
|
78
80
|
}
|
|
79
|
-
if (!options.isCreated && !options.path) {
|
|
80
|
-
throw new toolbox_1.CloudBaseError('请使用 --path
|
|
81
|
+
if (!options.isCreated && !options.path && !options.custom_image) {
|
|
82
|
+
throw new toolbox_1.CloudBaseError('请使用 --path 指定代码根目录或 --custom_image 指定 TCR 镜像 URL');
|
|
81
83
|
}
|
|
82
|
-
if (options.isCreated && !options.path && !options.library_image && !options.image) {
|
|
83
|
-
throw new toolbox_1.CloudBaseError('请使用 --path 指定代码根目录或 --library_image 指定线上镜像 tag ');
|
|
84
|
+
if (options.isCreated && !options.path && !options.custom_image && !options.library_image && !options.image) {
|
|
85
|
+
throw new toolbox_1.CloudBaseError('请使用 --path 指定代码根目录或 --custom_image 指定 TCR 镜像 URL 或 --library_image 指定线上镜像 tag ');
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
function tcbrServiceOptions(options, isCreated, defaultOverride, previousServerConfig) {
|
|
87
89
|
return __awaiter(this, void 0, void 0, function* () {
|
|
88
|
-
let { noConfirm: _noConfirm = false, override: _override = (defaultOverride || false), envId, serviceName, path, cpu, mem, minNum, maxNum, policyDetails, customLogs, envParams, containerPort, remark, targetDir, dockerfile, image, library_image, json: _json = false } = options;
|
|
90
|
+
let { noConfirm: _noConfirm = false, override: _override = (defaultOverride || false), envId, serviceName, path, cpu, mem, minNum, maxNum, policyDetails, customLogs, envParams, containerPort, remark, targetDir, dockerfile, image, custom_image, library_image, json: _json = false } = options;
|
|
89
91
|
checkRequiredParams({
|
|
90
92
|
envId,
|
|
91
93
|
serviceName,
|
|
92
94
|
containerPort,
|
|
93
95
|
isCreated,
|
|
94
96
|
path,
|
|
97
|
+
custom_image,
|
|
95
98
|
library_image,
|
|
96
99
|
image
|
|
97
100
|
});
|
|
98
101
|
containerPort = Number(containerPort);
|
|
102
|
+
const deployByImage = Boolean(custom_image || library_image || image);
|
|
99
103
|
const DeployInfo = {
|
|
100
|
-
DeployType:
|
|
101
|
-
? 'image'
|
|
102
|
-
: 'package',
|
|
104
|
+
DeployType: deployByImage ? types_1.DEPLOY_TYPE.IMAGE : types_1.DEPLOY_TYPE.PACKAGE,
|
|
103
105
|
DeployRemark: remark || '',
|
|
106
|
+
ReleaseType: 'FULL'
|
|
104
107
|
};
|
|
105
108
|
let { cpuConverted, memConverted, maxNumConverted, minNumConverted } = (0, utils_1.parseOptionalParams)({
|
|
106
109
|
cpu,
|
|
@@ -108,6 +111,7 @@ function tcbrServiceOptions(options, isCreated, defaultOverride, previousServerC
|
|
|
108
111
|
maxNum,
|
|
109
112
|
minNum
|
|
110
113
|
});
|
|
114
|
+
const defaultLogType = 'none';
|
|
111
115
|
const newServiceOptions = {
|
|
112
116
|
ServerName: serviceName,
|
|
113
117
|
EnvId: envId,
|
|
@@ -128,10 +132,11 @@ function tcbrServiceOptions(options, isCreated, defaultOverride, previousServerC
|
|
|
128
132
|
Port: containerPort,
|
|
129
133
|
HasDockerfile: true,
|
|
130
134
|
Dockerfile: dockerfile || 'Dockerfile',
|
|
135
|
+
LogType: defaultLogType
|
|
131
136
|
},
|
|
132
137
|
DeployInfo: Object.assign({}, DeployInfo)
|
|
133
138
|
};
|
|
134
|
-
if (DeployInfo.DeployType ===
|
|
139
|
+
if (DeployInfo.DeployType === types_1.DEPLOY_TYPE.PACKAGE) {
|
|
135
140
|
const { PackageName, PackageVersion } = yield (0, index_1.packageDeploy)({
|
|
136
141
|
envId,
|
|
137
142
|
serviceName,
|
|
@@ -140,20 +145,31 @@ function tcbrServiceOptions(options, isCreated, defaultOverride, previousServerC
|
|
|
140
145
|
DeployInfo.PackageName = PackageName;
|
|
141
146
|
DeployInfo.PackageVersion = PackageVersion;
|
|
142
147
|
}
|
|
143
|
-
else if (DeployInfo.DeployType ===
|
|
144
|
-
|
|
145
|
-
envId
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const imageInfo = imageList.find(({ Tag }) => Tag === library_image);
|
|
150
|
-
if (!imageInfo) {
|
|
151
|
-
throw new toolbox_1.CloudBaseError(`镜像 ${library_image} 不存在`);
|
|
148
|
+
else if (DeployInfo.DeployType === types_1.DEPLOY_TYPE.IMAGE) {
|
|
149
|
+
if (custom_image) {
|
|
150
|
+
const authorizedTcrInstances = yield getAuthorizedTcrInstance(envId);
|
|
151
|
+
if (!authorizedTcrInstances) {
|
|
152
|
+
const link = `https://console.cloud.tencent.com/tcbr/env?/tcbr/env=&envId=${envId}`;
|
|
153
|
+
throw new toolbox_1.CloudBaseError(`您还未授权 tcr 实例,请先到控制台授权:${(0, utils_1.genClickableLink)(link)}`);
|
|
152
154
|
}
|
|
153
|
-
|
|
155
|
+
yield validateTcrImageURL(authorizedTcrInstances, custom_image);
|
|
156
|
+
DeployInfo.ImageUrl = custom_image;
|
|
154
157
|
}
|
|
155
158
|
else {
|
|
156
|
-
|
|
159
|
+
const imageList = yield (0, __1.listImage)({
|
|
160
|
+
envId,
|
|
161
|
+
serviceName
|
|
162
|
+
});
|
|
163
|
+
if (library_image) {
|
|
164
|
+
const imageInfo = imageList.find(({ Tag }) => Tag === library_image);
|
|
165
|
+
if (!imageInfo) {
|
|
166
|
+
throw new toolbox_1.CloudBaseError(`镜像 ${library_image} 不存在`);
|
|
167
|
+
}
|
|
168
|
+
DeployInfo.ImageUrl = imageInfo.ImageUrl;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
throw new toolbox_1.CloudBaseError('暂不支持 --image 参数,请使用 --custom_image 指定 tcr 镜像 URL 或 --library_image 指定线上镜像 tag');
|
|
172
|
+
}
|
|
157
173
|
}
|
|
158
174
|
}
|
|
159
175
|
newServiceOptions.DeployInfo = Object.assign({}, DeployInfo);
|
|
@@ -161,3 +177,81 @@ function tcbrServiceOptions(options, isCreated, defaultOverride, previousServerC
|
|
|
161
177
|
});
|
|
162
178
|
}
|
|
163
179
|
exports.tcbrServiceOptions = tcbrServiceOptions;
|
|
180
|
+
function getAuthorizedTcrInstance(envId) {
|
|
181
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
182
|
+
let { data: { TcrInstances: authorizedTcrInstances } } = yield (0, utils_1.callTcbrApi)('DescribeTcrInstances', {
|
|
183
|
+
EnvId: envId
|
|
184
|
+
});
|
|
185
|
+
return authorizedTcrInstances;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
exports.getAuthorizedTcrInstance = getAuthorizedTcrInstance;
|
|
189
|
+
function validateTcrImageURL(authorizedTcrInstances, imageUrl) {
|
|
190
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
191
|
+
const errMsg = '镜像URL解析失败,请检查输入的镜像URL是否正确';
|
|
192
|
+
try {
|
|
193
|
+
const host = imageUrl.split('/')[0];
|
|
194
|
+
const namespace = imageUrl.split('/')[1];
|
|
195
|
+
const name = `${namespace}/${imageUrl.split('/')[2].split(':')[0]}`;
|
|
196
|
+
const tag = imageUrl.split('/')[2].split(':')[1];
|
|
197
|
+
const filteredInstances = authorizedTcrInstances === null || authorizedTcrInstances === void 0 ? void 0 : authorizedTcrInstances.filter(({ Domain }) => host === Domain);
|
|
198
|
+
if (!(filteredInstances === null || filteredInstances === void 0 ? void 0 : filteredInstances.length)) {
|
|
199
|
+
throw new toolbox_1.CloudBaseError(errMsg);
|
|
200
|
+
}
|
|
201
|
+
const reposUnderSpecifiedRegistry = [];
|
|
202
|
+
for (const registry of filteredInstances) {
|
|
203
|
+
const repos = [];
|
|
204
|
+
let { Id: registryId, Domain: domain } = registry;
|
|
205
|
+
const limit = 100;
|
|
206
|
+
let curIndex = 1;
|
|
207
|
+
let totalCount = 0;
|
|
208
|
+
do {
|
|
209
|
+
const rsp = yield tcrCloudApiService.request('DescribeRepositories', {
|
|
210
|
+
RegistryId: registryId,
|
|
211
|
+
Offset: curIndex,
|
|
212
|
+
Limit: limit
|
|
213
|
+
});
|
|
214
|
+
repos.push(...rsp.RepositoryList);
|
|
215
|
+
curIndex += 1;
|
|
216
|
+
totalCount = rsp.TotalCount;
|
|
217
|
+
} while (repos.length < totalCount);
|
|
218
|
+
reposUnderSpecifiedRegistry.push({ registryId, domain, repos });
|
|
219
|
+
}
|
|
220
|
+
const filteredRepos = [];
|
|
221
|
+
for (const repo of reposUnderSpecifiedRegistry) {
|
|
222
|
+
const { registryId, repos } = repo;
|
|
223
|
+
filteredRepos.push(...repos.filter(({ Name }) => Name === name));
|
|
224
|
+
if (!(filteredRepos === null || filteredRepos === void 0 ? void 0 : filteredRepos.length)) {
|
|
225
|
+
throw new toolbox_1.CloudBaseError(errMsg);
|
|
226
|
+
}
|
|
227
|
+
filteredRepos.forEach(item => { item.registryId = registryId; });
|
|
228
|
+
}
|
|
229
|
+
for (const repoItem of filteredRepos) {
|
|
230
|
+
const { Name, Namespace, registryId } = repoItem;
|
|
231
|
+
const images = [];
|
|
232
|
+
const limit = 100;
|
|
233
|
+
let curIndex = 1;
|
|
234
|
+
let totalCount = 0;
|
|
235
|
+
do {
|
|
236
|
+
const rsp = yield tcrCloudApiService.request('DescribeImages', {
|
|
237
|
+
RegistryId: registryId,
|
|
238
|
+
NamespaceName: Namespace,
|
|
239
|
+
RepositoryName: Name.split(`${Namespace}/`)[1],
|
|
240
|
+
Offset: curIndex,
|
|
241
|
+
Limit: limit
|
|
242
|
+
});
|
|
243
|
+
images.push(...rsp.ImageInfoList);
|
|
244
|
+
curIndex += 1;
|
|
245
|
+
totalCount = rsp.TotalCount;
|
|
246
|
+
} while (images.length < totalCount);
|
|
247
|
+
if (!images.some(({ ImageVersion }) => ImageVersion === tag)) {
|
|
248
|
+
throw new toolbox_1.CloudBaseError(errMsg);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
catch (e) {
|
|
253
|
+
throw new toolbox_1.CloudBaseError(errMsg);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
exports.validateTcrImageURL = validateTcrImageURL;
|
package/lib/types.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ServerLanguageType = void 0;
|
|
3
|
+
exports.DEPLOY_TYPE = exports.ServerLanguageType = void 0;
|
|
4
4
|
var ServerLanguageType;
|
|
5
5
|
(function (ServerLanguageType) {
|
|
6
6
|
ServerLanguageType["node"] = "node";
|
|
7
7
|
})(ServerLanguageType = exports.ServerLanguageType || (exports.ServerLanguageType = {}));
|
|
8
|
+
var DEPLOY_TYPE;
|
|
9
|
+
(function (DEPLOY_TYPE) {
|
|
10
|
+
DEPLOY_TYPE["PACKAGE"] = "package";
|
|
11
|
+
DEPLOY_TYPE["IMAGE"] = "image";
|
|
12
|
+
})(DEPLOY_TYPE = exports.DEPLOY_TYPE || (exports.DEPLOY_TYPE = {}));
|
package/package.json
CHANGED
|
@@ -82,6 +82,10 @@ export class CreateServiceTcbr extends Command {
|
|
|
82
82
|
// flags: '--library_image <library_image>',
|
|
83
83
|
// desc: '线上镜像仓库的 tag'
|
|
84
84
|
// },
|
|
85
|
+
{
|
|
86
|
+
flags: '--custom_image <custom_image>',
|
|
87
|
+
desc: 'TCR 仓库镜像 URL'
|
|
88
|
+
},
|
|
85
89
|
{
|
|
86
90
|
flags: '--image <image>',
|
|
87
91
|
desc: '镜像标签或ID'
|
|
@@ -80,6 +80,10 @@ export class DeployServiceTcbr extends Command {
|
|
|
80
80
|
flags: '--dockerfile <dockerfile>',
|
|
81
81
|
desc: 'Dockerfile文件名,默认为 Dockerfile'
|
|
82
82
|
},
|
|
83
|
+
{
|
|
84
|
+
flags: '--custom_image <custom_image>',
|
|
85
|
+
desc: 'TCR 仓库镜像 URL'
|
|
86
|
+
},
|
|
83
87
|
{
|
|
84
88
|
flags: '--library_image <library_image>',
|
|
85
89
|
desc: '线上镜像仓库的 tag,仅在服务已存在时可用'
|
|
@@ -48,9 +48,9 @@ export class ListServiceTcbr extends Command {
|
|
|
48
48
|
envId: envId
|
|
49
49
|
})
|
|
50
50
|
loading.stop()
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
const specificServer = serverList.filter(serverItem => serverItem.ServerName === serviceName)
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
// 打印 JSON 形式结果
|
|
55
55
|
if (options.json) {
|
|
56
56
|
console.log(JSON.stringify({
|
|
@@ -62,12 +62,12 @@ export class ListServiceTcbr extends Command {
|
|
|
62
62
|
}, null, 2))
|
|
63
63
|
return
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
if (!serverList.length) {
|
|
67
67
|
console.log('当前环境下没有服务')
|
|
68
68
|
return
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
// 如有指定服务名,则只打印指定服务的信息
|
|
72
72
|
let tableData: (string | number)[][]
|
|
73
73
|
if (specificServer.length) {
|
|
@@ -72,6 +72,10 @@ export class UpdateServiceTcbr extends Command {
|
|
|
72
72
|
flags: '--dockerfile <dockerfile>',
|
|
73
73
|
desc: 'Dockerfile文件名,默认为 Dockerfile'
|
|
74
74
|
},
|
|
75
|
+
{
|
|
76
|
+
flags: '--custom_image <custom_image>',
|
|
77
|
+
desc: 'TCR 仓库镜像 URL'
|
|
78
|
+
},
|
|
75
79
|
{
|
|
76
80
|
flags: '--library_image <library_image>',
|
|
77
81
|
desc: '线上镜像仓库的 tag'
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { CloudApiService, parseOptionalParams, parseInputParam } from '../../utils'
|
|
2
|
-
import { ITcbrServiceOptions, IDescribeWxCloudBaseRunReleaseOrder, ITcbrServiceRequiredOptions } from '../../types'
|
|
1
|
+
import { CloudApiService, parseOptionalParams, parseInputParam, callTcbrApi, genClickableLink } from '../../utils'
|
|
2
|
+
import { ITcbrServiceOptions, IDescribeWxCloudBaseRunReleaseOrder, ITcbrServiceRequiredOptions, DEPLOY_TYPE, IAuthorizedTcrInstance } from '../../types'
|
|
3
3
|
import { CloudBaseError } from '@cloudbase/toolbox'
|
|
4
4
|
import { packageDeploy } from './index'
|
|
5
5
|
import { listImage } from '..'
|
|
6
6
|
import { DEFAULT_CPU_MEM_SET } from '../../constant'
|
|
7
7
|
|
|
8
8
|
const tcbService = CloudApiService.getInstance('tcb')
|
|
9
|
+
const tcrCloudApiService = new CloudApiService('tcr', {}, '2019-09-24')
|
|
9
10
|
|
|
10
11
|
export async function describeWxCloudBaseRunReleaseOrder(options: IDescribeWxCloudBaseRunReleaseOrder) {
|
|
11
12
|
const res = await tcbService.request('DescribeWxCloudBaseRunReleaseOrder', {
|
|
@@ -74,12 +75,12 @@ function checkRequiredParams(options: ITcbrServiceRequiredOptions) {
|
|
|
74
75
|
throw new CloudBaseError('必须使用 --containerPort 指定监听端口号')
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
if (!options.isCreated && !options.path) {
|
|
78
|
-
throw new CloudBaseError('请使用 --path
|
|
78
|
+
if (!options.isCreated && !options.path && !options.custom_image) {
|
|
79
|
+
throw new CloudBaseError('请使用 --path 指定代码根目录或 --custom_image 指定 TCR 镜像 URL')
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
if (options.isCreated && !options.path && !options.library_image && !options.image) {
|
|
82
|
-
throw new CloudBaseError('请使用 --path 指定代码根目录或 --library_image 指定线上镜像 tag ')
|
|
82
|
+
if (options.isCreated && !options.path && !options.custom_image && !options.library_image && !options.image) {
|
|
83
|
+
throw new CloudBaseError('请使用 --path 指定代码根目录或 --custom_image 指定 TCR 镜像 URL 或 --library_image 指定线上镜像 tag ')
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
|
|
@@ -110,6 +111,7 @@ export async function tcbrServiceOptions(options: ITcbrServiceOptions, isCreated
|
|
|
110
111
|
targetDir,
|
|
111
112
|
dockerfile,
|
|
112
113
|
image,
|
|
114
|
+
custom_image,
|
|
113
115
|
library_image,
|
|
114
116
|
json: _json = false
|
|
115
117
|
} = options
|
|
@@ -121,18 +123,20 @@ export async function tcbrServiceOptions(options: ITcbrServiceOptions, isCreated
|
|
|
121
123
|
containerPort,
|
|
122
124
|
isCreated,
|
|
123
125
|
path,
|
|
126
|
+
custom_image,
|
|
124
127
|
library_image,
|
|
125
128
|
image
|
|
126
129
|
})
|
|
127
130
|
containerPort = Number(containerPort)
|
|
128
131
|
|
|
129
|
-
//
|
|
132
|
+
// 处理用户传入参数
|
|
133
|
+
const deployByImage = Boolean(custom_image || library_image || image)
|
|
130
134
|
const DeployInfo: any = {
|
|
131
|
-
DeployType:
|
|
132
|
-
? 'image'
|
|
133
|
-
: 'package',
|
|
135
|
+
DeployType: deployByImage ? DEPLOY_TYPE.IMAGE : DEPLOY_TYPE.PACKAGE,
|
|
134
136
|
DeployRemark: remark || '',
|
|
137
|
+
ReleaseType: 'FULL'
|
|
135
138
|
}
|
|
139
|
+
|
|
136
140
|
// 可选参数进行校验和转换
|
|
137
141
|
let {
|
|
138
142
|
cpuConverted,
|
|
@@ -146,6 +150,7 @@ export async function tcbrServiceOptions(options: ITcbrServiceOptions, isCreated
|
|
|
146
150
|
minNum
|
|
147
151
|
})
|
|
148
152
|
|
|
153
|
+
const defaultLogType = 'none'
|
|
149
154
|
const newServiceOptions = {
|
|
150
155
|
ServerName: serviceName,
|
|
151
156
|
EnvId: envId,
|
|
@@ -166,13 +171,14 @@ export async function tcbrServiceOptions(options: ITcbrServiceOptions, isCreated
|
|
|
166
171
|
Port: containerPort,
|
|
167
172
|
HasDockerfile: true,
|
|
168
173
|
Dockerfile: dockerfile || 'Dockerfile',
|
|
174
|
+
LogType: defaultLogType
|
|
169
175
|
},
|
|
170
176
|
DeployInfo: {
|
|
171
177
|
...DeployInfo
|
|
172
178
|
}
|
|
173
179
|
}
|
|
174
180
|
|
|
175
|
-
if (DeployInfo.DeployType ===
|
|
181
|
+
if (DeployInfo.DeployType === DEPLOY_TYPE.PACKAGE) {
|
|
176
182
|
// 本地上传
|
|
177
183
|
const { PackageName, PackageVersion } = await packageDeploy({
|
|
178
184
|
envId,
|
|
@@ -181,21 +187,34 @@ export async function tcbrServiceOptions(options: ITcbrServiceOptions, isCreated
|
|
|
181
187
|
})
|
|
182
188
|
DeployInfo.PackageName = PackageName
|
|
183
189
|
DeployInfo.PackageVersion = PackageVersion
|
|
184
|
-
} else if (DeployInfo.DeployType ===
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
envId
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const imageInfo = imageList.find(({ Tag }) => Tag === library_image)
|
|
192
|
-
if (!imageInfo) {
|
|
193
|
-
throw new CloudBaseError(`镜像 ${library_image} 不存在`)
|
|
190
|
+
} else if (DeployInfo.DeployType === DEPLOY_TYPE.IMAGE) {
|
|
191
|
+
// 传入 tcr 镜像实例
|
|
192
|
+
if (custom_image) {
|
|
193
|
+
const authorizedTcrInstances = await getAuthorizedTcrInstance(envId)
|
|
194
|
+
if (!authorizedTcrInstances) {
|
|
195
|
+
const link = `https://console.cloud.tencent.com/tcbr/env?/tcbr/env=&envId=${envId}`
|
|
196
|
+
throw new CloudBaseError(`您还未授权 tcr 实例,请先到控制台授权:${genClickableLink(link)}`)
|
|
194
197
|
}
|
|
195
|
-
|
|
198
|
+
// 检查传入的 URL 是否合法,不合法 throw error 阻止执行
|
|
199
|
+
await validateTcrImageURL(authorizedTcrInstances, custom_image)
|
|
200
|
+
|
|
201
|
+
DeployInfo.ImageUrl = custom_image
|
|
196
202
|
} else {
|
|
197
|
-
//
|
|
198
|
-
|
|
203
|
+
// 拉取镜像
|
|
204
|
+
const imageList = await listImage({
|
|
205
|
+
envId,
|
|
206
|
+
serviceName
|
|
207
|
+
})
|
|
208
|
+
if (library_image) {
|
|
209
|
+
const imageInfo = imageList.find(({ Tag }) => Tag === library_image)
|
|
210
|
+
if (!imageInfo) {
|
|
211
|
+
throw new CloudBaseError(`镜像 ${library_image} 不存在`)
|
|
212
|
+
}
|
|
213
|
+
DeployInfo.ImageUrl = imageInfo.ImageUrl
|
|
214
|
+
} else {
|
|
215
|
+
// 暂时不支持 image 镜像
|
|
216
|
+
throw new CloudBaseError('暂不支持 --image 参数,请使用 --custom_image 指定 tcr 镜像 URL 或 --library_image 指定线上镜像 tag')
|
|
217
|
+
}
|
|
199
218
|
}
|
|
200
219
|
}
|
|
201
220
|
|
|
@@ -204,3 +223,89 @@ export async function tcbrServiceOptions(options: ITcbrServiceOptions, isCreated
|
|
|
204
223
|
}
|
|
205
224
|
return newServiceOptions
|
|
206
225
|
}
|
|
226
|
+
|
|
227
|
+
// 获取授权 tcr 实例
|
|
228
|
+
export async function getAuthorizedTcrInstance(envId: string): Promise<IAuthorizedTcrInstance[] | null> {
|
|
229
|
+
let { data: { TcrInstances: authorizedTcrInstances } } = await callTcbrApi('DescribeTcrInstances', {
|
|
230
|
+
EnvId: envId
|
|
231
|
+
})
|
|
232
|
+
return authorizedTcrInstances
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
*
|
|
237
|
+
* @description 校验输入的 tcr 镜像 URL 是否合法(域名/仓库名:镜像tag)
|
|
238
|
+
* @param authorizedTcrInstances 已授权的 tcr 实例列表
|
|
239
|
+
* @param imageUrl 输入的镜像 URL 地址
|
|
240
|
+
*/
|
|
241
|
+
export async function validateTcrImageURL(authorizedTcrInstances: IAuthorizedTcrInstance[] | null, imageUrl: string) {
|
|
242
|
+
const errMsg = '镜像URL解析失败,请检查输入的镜像URL是否正确'
|
|
243
|
+
try {
|
|
244
|
+
const host = imageUrl.split('/')[0]
|
|
245
|
+
const namespace = imageUrl.split('/')[1]
|
|
246
|
+
const name = `${namespace}/${imageUrl.split('/')[2].split(':')[0]}`
|
|
247
|
+
const tag = imageUrl.split('/')[2].split(':')[1]
|
|
248
|
+
// 获取授权实例,校验域名
|
|
249
|
+
const filteredInstances = authorizedTcrInstances?.filter(({ Domain }) => host === Domain)
|
|
250
|
+
|
|
251
|
+
if (!filteredInstances?.length) {
|
|
252
|
+
throw new CloudBaseError(errMsg)
|
|
253
|
+
}
|
|
254
|
+
// 获取实例中仓库,校验仓库名
|
|
255
|
+
const reposUnderSpecifiedRegistry = []
|
|
256
|
+
for (const registry of filteredInstances) {
|
|
257
|
+
const repos = []
|
|
258
|
+
let { Id: registryId, Domain: domain } = registry
|
|
259
|
+
const limit = 100
|
|
260
|
+
let curIndex = 1
|
|
261
|
+
let totalCount = 0
|
|
262
|
+
do {
|
|
263
|
+
const rsp = await tcrCloudApiService.request('DescribeRepositories', {
|
|
264
|
+
RegistryId: registryId,
|
|
265
|
+
Offset: curIndex,
|
|
266
|
+
Limit: limit
|
|
267
|
+
})
|
|
268
|
+
repos.push(...rsp.RepositoryList)
|
|
269
|
+
curIndex += 1
|
|
270
|
+
totalCount = rsp.TotalCount
|
|
271
|
+
} while (repos.length < totalCount)
|
|
272
|
+
reposUnderSpecifiedRegistry.push({ registryId, domain, repos })
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const filteredRepos = []
|
|
276
|
+
for (const repo of reposUnderSpecifiedRegistry) {
|
|
277
|
+
const { registryId, repos } = repo
|
|
278
|
+
filteredRepos.push(...repos.filter(({ Name }) => Name === name))
|
|
279
|
+
if (!filteredRepos?.length) {
|
|
280
|
+
throw new CloudBaseError(errMsg)
|
|
281
|
+
}
|
|
282
|
+
filteredRepos.forEach(item => { item.registryId = registryId }) // 手动插入实例id,获取镜像接口用
|
|
283
|
+
}
|
|
284
|
+
// 获取仓库内镜像,校验tag
|
|
285
|
+
for (const repoItem of filteredRepos) {
|
|
286
|
+
const { Name, Namespace, registryId } = repoItem
|
|
287
|
+
const images = []
|
|
288
|
+
const limit = 100
|
|
289
|
+
let curIndex = 1
|
|
290
|
+
let totalCount = 0
|
|
291
|
+
do {
|
|
292
|
+
const rsp = await tcrCloudApiService.request('DescribeImages', {
|
|
293
|
+
RegistryId: registryId,
|
|
294
|
+
NamespaceName: Namespace,
|
|
295
|
+
RepositoryName: Name.split(`${Namespace}/`)[1],
|
|
296
|
+
Offset: curIndex,
|
|
297
|
+
Limit: limit
|
|
298
|
+
})
|
|
299
|
+
images.push(...rsp.ImageInfoList)
|
|
300
|
+
curIndex += 1
|
|
301
|
+
totalCount = rsp.TotalCount
|
|
302
|
+
} while (images.length < totalCount)
|
|
303
|
+
|
|
304
|
+
if (!images.some(({ ImageVersion }) => ImageVersion === tag)) {
|
|
305
|
+
throw new CloudBaseError(errMsg)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
} catch (e) {
|
|
309
|
+
throw new CloudBaseError(errMsg)
|
|
310
|
+
}
|
|
311
|
+
}
|
|
@@ -11,7 +11,7 @@ export const describeCloudRunServerDetail = async (options: { envId: string; ser
|
|
|
11
11
|
})
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export async function createCloudRunServer(serviceConfigOptions) {
|
|
14
|
+
export async function createCloudRunServer(serviceConfigOptions: Record<string, any>) {
|
|
15
15
|
return callTcbrApi('CreateCloudRunServer', serviceConfigOptions)
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CloudBaseError, logger } from '@cloudbase/toolbox'
|
|
2
2
|
import inquirer from 'inquirer'
|
|
3
3
|
import { describeCloudRunServerDetail } from '..'
|
|
4
|
+
import { ITcbrServiceOptions } from '../../types'
|
|
4
5
|
import { callTcbrApi } from '../../utils'
|
|
5
6
|
import { tcbrServiceOptions } from './common'
|
|
6
7
|
import { getBuildStatus, getLogs } from './showLogs'
|
|
@@ -14,7 +15,7 @@ export async function updateCloudRunServer(serviceConfigOptions) {
|
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
export async function updateTcbrService(options) {
|
|
18
|
+
export async function updateTcbrService(options: ITcbrServiceOptions) {
|
|
18
19
|
const { data: serviceDetail } = await describeCloudRunServerDetail({
|
|
19
20
|
envId: options.envId,
|
|
20
21
|
serviceName: options.serviceName
|
package/src/types.ts
CHANGED
|
@@ -585,6 +585,7 @@ export interface ITcbrServiceOptions {
|
|
|
585
585
|
targetDir: string,
|
|
586
586
|
dockerfile: string,
|
|
587
587
|
image: string,
|
|
588
|
+
custom_image: string,
|
|
588
589
|
library_image: string,
|
|
589
590
|
json: boolean
|
|
590
591
|
}
|
|
@@ -640,6 +641,7 @@ export interface ITcbrServiceRequiredOptions {
|
|
|
640
641
|
containerPort: number,
|
|
641
642
|
isCreated: boolean,
|
|
642
643
|
path: string,
|
|
644
|
+
custom_image: string,
|
|
643
645
|
library_image: string,
|
|
644
646
|
image: string
|
|
645
647
|
}
|
|
@@ -656,4 +658,15 @@ export interface ITcbrServiceConvertedOptionalOptions {
|
|
|
656
658
|
memConverted: number,
|
|
657
659
|
maxNumConverted: number,
|
|
658
660
|
minNumConverted: number
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
export interface IAuthorizedTcrInstance {
|
|
664
|
+
Id: string,
|
|
665
|
+
Name: string,
|
|
666
|
+
Domain: string
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
export enum DEPLOY_TYPE {
|
|
670
|
+
PACKAGE = 'package',
|
|
671
|
+
IMAGE = 'image'
|
|
659
672
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ITcbrServiceOptions, IDescribeWxCloudBaseRunReleaseOrder } from '../../types';
|
|
1
|
+
import { ITcbrServiceOptions, IDescribeWxCloudBaseRunReleaseOrder, IAuthorizedTcrInstance } from '../../types';
|
|
2
2
|
export declare function describeWxCloudBaseRunReleaseOrder(options: IDescribeWxCloudBaseRunReleaseOrder): Promise<any>;
|
|
3
3
|
export declare const convertNumber: (item: any) => number;
|
|
4
4
|
export declare const extractPolicyDetails: (policyDetails: string) => {
|
|
@@ -27,6 +27,9 @@ export declare function tcbrServiceOptions(options: ITcbrServiceOptions, isCreat
|
|
|
27
27
|
Port: number;
|
|
28
28
|
HasDockerfile: boolean;
|
|
29
29
|
Dockerfile: string;
|
|
30
|
+
LogType: string;
|
|
30
31
|
};
|
|
31
32
|
DeployInfo: any;
|
|
32
33
|
}>;
|
|
34
|
+
export declare function getAuthorizedTcrInstance(envId: string): Promise<IAuthorizedTcrInstance[] | null>;
|
|
35
|
+
export declare function validateTcrImageURL(authorizedTcrInstances: IAuthorizedTcrInstance[] | null, imageUrl: string): Promise<void>;
|
|
@@ -3,5 +3,5 @@ export declare const describeCloudRunServerDetail: (options: {
|
|
|
3
3
|
envId: string;
|
|
4
4
|
serviceName: string;
|
|
5
5
|
}) => Promise<any>;
|
|
6
|
-
export declare function createCloudRunServer(serviceConfigOptions: any): Promise<any>;
|
|
6
|
+
export declare function createCloudRunServer(serviceConfigOptions: Record<string, any>): Promise<any>;
|
|
7
7
|
export declare function createTcbrService(options: ITcbrServiceOptions): Promise<void>;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
+
import { ITcbrServiceOptions } from '../../types';
|
|
1
2
|
export declare function updateCloudRunServer(serviceConfigOptions: any): Promise<any>;
|
|
2
|
-
export declare function updateTcbrService(options:
|
|
3
|
+
export declare function updateTcbrService(options: ITcbrServiceOptions): Promise<void>;
|
package/types/types.d.ts
CHANGED
|
@@ -509,6 +509,7 @@ export interface ITcbrServiceOptions {
|
|
|
509
509
|
targetDir: string;
|
|
510
510
|
dockerfile: string;
|
|
511
511
|
image: string;
|
|
512
|
+
custom_image: string;
|
|
512
513
|
library_image: string;
|
|
513
514
|
json: boolean;
|
|
514
515
|
}
|
|
@@ -557,6 +558,7 @@ export interface ITcbrServiceRequiredOptions {
|
|
|
557
558
|
containerPort: number;
|
|
558
559
|
isCreated: boolean;
|
|
559
560
|
path: string;
|
|
561
|
+
custom_image: string;
|
|
560
562
|
library_image: string;
|
|
561
563
|
image: string;
|
|
562
564
|
}
|
|
@@ -572,3 +574,12 @@ export interface ITcbrServiceConvertedOptionalOptions {
|
|
|
572
574
|
maxNumConverted: number;
|
|
573
575
|
minNumConverted: number;
|
|
574
576
|
}
|
|
577
|
+
export interface IAuthorizedTcrInstance {
|
|
578
|
+
Id: string;
|
|
579
|
+
Name: string;
|
|
580
|
+
Domain: string;
|
|
581
|
+
}
|
|
582
|
+
export declare enum DEPLOY_TYPE {
|
|
583
|
+
PACKAGE = "package",
|
|
584
|
+
IMAGE = "image"
|
|
585
|
+
}
|