@cloudbase/manager-node 4.3.1 → 4.3.2
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/cloudrun/index.js +216 -4
- package/lib/utils/index.js +27 -17
- package/package.json +1 -1
- package/types/cloudrun/index.d.ts +31 -1
package/lib/cloudrun/index.js
CHANGED
|
@@ -10,6 +10,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.CloudRunService = void 0;
|
|
13
|
+
const archiver_1 = __importDefault(require("archiver"));
|
|
14
|
+
const fs_extra_1 = require("fs-extra");
|
|
13
15
|
const path_1 = __importDefault(require("path"));
|
|
14
16
|
const utils_1 = require("../utils");
|
|
15
17
|
/**
|
|
@@ -63,10 +65,7 @@ class CloudRunService {
|
|
|
63
65
|
/**
|
|
64
66
|
* 获取最新版本
|
|
65
67
|
*/
|
|
66
|
-
const cloudRunServerDetailRes = await this.
|
|
67
|
-
EnvId: envConfig.EnvId,
|
|
68
|
-
ServerName: serverName
|
|
69
|
-
});
|
|
68
|
+
const cloudRunServerDetailRes = await this.detail({ serverName });
|
|
70
69
|
const version = (_b = (_a = cloudRunServerDetailRes.OnlineVersionInfos) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.VersionName;
|
|
71
70
|
if (!version) {
|
|
72
71
|
throw new Error('未找到云托管服务版本');
|
|
@@ -131,6 +130,109 @@ class CloudRunService {
|
|
|
131
130
|
ServerName: params.serverName // 要删除的服务名称
|
|
132
131
|
});
|
|
133
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* 本地代码部署云托管服务
|
|
135
|
+
* @param {Object} params 部署参数
|
|
136
|
+
* @param {string} params.serverName 要部署的服务名称
|
|
137
|
+
* @param {string} params.targetPath 本地代码路径
|
|
138
|
+
* @param {Object} [params.serverConfig] 服务配置项(可选)
|
|
139
|
+
* @param {string[]} [params.serverConfig.OpenAccessTypes] 开放访问类型
|
|
140
|
+
* @param {number} [params.serverConfig.Cpu] CPU规格
|
|
141
|
+
* @param {number} [params.serverConfig.Mem] 内存规格
|
|
142
|
+
* @param {number} [params.serverConfig.MinNum] 最小实例数
|
|
143
|
+
* @param {number} [params.serverConfig.MaxNum] 最大实例数
|
|
144
|
+
* @param {Object} [params.serverConfig.PolicyDetails] 策略详情
|
|
145
|
+
* @param {Object} [params.serverConfig.CustomLogs] 自定义日志配置
|
|
146
|
+
* @param {Object} [params.serverConfig.EnvParams] 环境变量参数
|
|
147
|
+
* @param {number} [params.serverConfig.Port] 端口(函数型服务不允许设置)
|
|
148
|
+
* @param {string} [params.serverConfig.Dockerfile] Dockerfile路径
|
|
149
|
+
* @param {string} [params.serverConfig.BuildDir] 构建目录
|
|
150
|
+
* @param {boolean} [params.serverConfig.InternalAccess] 是否开启内网访问
|
|
151
|
+
* @param {string} [params.serverConfig.InternalDomain] 内网域名
|
|
152
|
+
* @param {string} [params.serverConfig.EntryPoint] 入口文件
|
|
153
|
+
* @param {string} [params.serverConfig.Cmd] 启动命令
|
|
154
|
+
* @returns {Promise<IResponseInfo>} 返回部署操作的响应信息
|
|
155
|
+
*/
|
|
156
|
+
async deploy(params) {
|
|
157
|
+
const { serverName, targetPath = process.cwd(), serverConfig } = params;
|
|
158
|
+
/**
|
|
159
|
+
* 参数校验和默认值设置
|
|
160
|
+
*/
|
|
161
|
+
if (!serverName) {
|
|
162
|
+
throw new Error('Missing required parameters: serviceName');
|
|
163
|
+
}
|
|
164
|
+
// 获取当前环境配置(包含EnvId)
|
|
165
|
+
const envConfig = this.environment.lazyEnvironmentConfig;
|
|
166
|
+
/**
|
|
167
|
+
* 获取部署包上传信息
|
|
168
|
+
*/
|
|
169
|
+
const { UploadUrl: uploadUrl, UploadHeaders: uploadHeaders, PackageName: packageName, PackageVersion: packageVersion } = await this.tcbService.request('DescribeCloudBaseBuildService', {
|
|
170
|
+
EnvId: envConfig.EnvId,
|
|
171
|
+
ServiceName: serverName
|
|
172
|
+
});
|
|
173
|
+
const deployInfo = {
|
|
174
|
+
DeployType: 'package',
|
|
175
|
+
PackageName: packageName,
|
|
176
|
+
PackageVersion: packageVersion
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* 上传部署包
|
|
180
|
+
*/
|
|
181
|
+
const zipFile = await codeToZip(targetPath, { installDependency: true });
|
|
182
|
+
await upload({
|
|
183
|
+
action: uploadUrl,
|
|
184
|
+
file: zipFile,
|
|
185
|
+
filename: 'file',
|
|
186
|
+
headers: (uploadHeaders || []).reduce((map, item) => {
|
|
187
|
+
map[item.Key] = item.Value;
|
|
188
|
+
return map;
|
|
189
|
+
}, {}) || {},
|
|
190
|
+
method: 'PUT',
|
|
191
|
+
send: (file) => file
|
|
192
|
+
});
|
|
193
|
+
/**
|
|
194
|
+
* 执行部署
|
|
195
|
+
*/
|
|
196
|
+
if (await this._checkFunctionExist(serverName)) {
|
|
197
|
+
// 更新
|
|
198
|
+
const serverDetail = await this.detail({ serverName });
|
|
199
|
+
const _serverConfig = Object.assign(Object.assign(Object.assign({}, ((serverDetail === null || serverDetail === void 0 ? void 0 : serverDetail.ServerConfig) || {})), serverConfig), ((serverDetail === null || serverDetail === void 0 ? void 0 : serverDetail.ServerConfig.Tag) === 'function:' ? { Port: 3000 } : {}) // 函数型不能指定端口,需要固定为3000
|
|
200
|
+
);
|
|
201
|
+
deployInfo.ReleaseType = 'FULL';
|
|
202
|
+
return this._upsertFunction(false, {
|
|
203
|
+
name: serverName,
|
|
204
|
+
deployInfo,
|
|
205
|
+
serverConfig: _serverConfig
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
// 创建
|
|
210
|
+
/**
|
|
211
|
+
* 判断是容器器还是函数型
|
|
212
|
+
*/
|
|
213
|
+
let type = 'function';
|
|
214
|
+
if (serverConfig === null || serverConfig === void 0 ? void 0 : serverConfig.Dockerfile) {
|
|
215
|
+
type = 'container';
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
if (await (0, fs_extra_1.pathExists)(path_1.default.join(targetPath, 'Dockerfile'))) {
|
|
219
|
+
type = 'container';
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (type === 'function') {
|
|
223
|
+
deployInfo.BuildPacks = {
|
|
224
|
+
LanguageVersion: '20.18',
|
|
225
|
+
RepoLanguage: 'Node.js'
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const _serverConfig = Object.assign(Object.assign(Object.assign({ OpenAccessTypes: ['OA', 'PUBLIC'], Cpu: 0, Mem: 0, MinNum: 0, MaxNum: 0, PolicyDetails: [], EnvParams: JSON.stringify({}), InitialDelaySeconds: 0, CustomLogs: '', HasDockerfile: true, CreateTime: '', EnvId: envConfig.EnvId, ServerName: serverName, Port: type === 'container' ? 80 : 3000, Dockerfile: 'Dockerfile', BuildDir: '' }, serverConfig), (type === 'function' ? { Port: 3000 } : {})), { Tag: type === 'container' ? '' : 'function:' });
|
|
229
|
+
return this._upsertFunction(true, {
|
|
230
|
+
name: serverName,
|
|
231
|
+
deployInfo,
|
|
232
|
+
serverConfig: _serverConfig
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
134
236
|
/**
|
|
135
237
|
* 获取云托管服务模板列表
|
|
136
238
|
* @returns {Promise<ITemplate[]>} 返回模板数组
|
|
@@ -138,6 +240,32 @@ class CloudRunService {
|
|
|
138
240
|
async getTemplates() {
|
|
139
241
|
return (0, utils_1.fetchTemplates)(['tcbrFunc', 'tcbrContainer']);
|
|
140
242
|
}
|
|
243
|
+
async _checkFunctionExist(name) {
|
|
244
|
+
try {
|
|
245
|
+
await this.detail({
|
|
246
|
+
serverName: name
|
|
247
|
+
});
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
catch (e) {
|
|
251
|
+
if (e.code === 'ResourceNotFound' ||
|
|
252
|
+
// 备注:以下条件当 NotFound 处理(已与 fisheryan 确认过)
|
|
253
|
+
(e.code === 'InvalidParameter' && e.original.Message === 'service data illegal')) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
throw e;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
_upsertFunction(isNew, data) {
|
|
260
|
+
const { name, deployInfo, serverConfig } = data;
|
|
261
|
+
const envConfig = this.environment.lazyEnvironmentConfig;
|
|
262
|
+
return this.tcbrService.request(isNew ? 'CreateCloudRunServer' : 'UpdateCloudRunServer', {
|
|
263
|
+
EnvId: envConfig.EnvId,
|
|
264
|
+
ServerName: name,
|
|
265
|
+
DeployInfo: deployInfo,
|
|
266
|
+
ServerConfig: serverConfig
|
|
267
|
+
});
|
|
268
|
+
}
|
|
141
269
|
}
|
|
142
270
|
exports.CloudRunService = CloudRunService;
|
|
143
271
|
__decorate([
|
|
@@ -155,3 +283,87 @@ __decorate([
|
|
|
155
283
|
__decorate([
|
|
156
284
|
(0, utils_1.preLazy)()
|
|
157
285
|
], CloudRunService.prototype, "delete", null);
|
|
286
|
+
__decorate([
|
|
287
|
+
(0, utils_1.preLazy)()
|
|
288
|
+
], CloudRunService.prototype, "deploy", null);
|
|
289
|
+
async function codeToZip(cwd, options) {
|
|
290
|
+
const archive = (0, archiver_1.default)('zip', {
|
|
291
|
+
zlib: { level: 1 } // 保持与之前相同的压缩级别
|
|
292
|
+
});
|
|
293
|
+
const chunks = [];
|
|
294
|
+
const bufferPromise = new Promise((resolve, reject) => {
|
|
295
|
+
archive.on('data', (chunk) => {
|
|
296
|
+
chunks.push(chunk);
|
|
297
|
+
});
|
|
298
|
+
archive.on('end', () => {
|
|
299
|
+
resolve(Buffer.concat(chunks));
|
|
300
|
+
});
|
|
301
|
+
archive.on('error', err => {
|
|
302
|
+
reject(err);
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
async function addFilesToArchive(dir, root = false, relativePath = '') {
|
|
306
|
+
const entries = await (0, fs_extra_1.readdir)(dir, { withFileTypes: true });
|
|
307
|
+
for (let entry of entries) {
|
|
308
|
+
const fullPath = path_1.default.join(dir, entry.name);
|
|
309
|
+
const entryRelativePath = path_1.default.join(relativePath, entry.name);
|
|
310
|
+
if (entry.isDirectory()) {
|
|
311
|
+
if (['logs', '.git'].includes(entry.name)) {
|
|
312
|
+
// 忽略 logs 等目录
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (options === null || options === void 0 ? void 0 : options.installDependency) {
|
|
316
|
+
// 忽略 node_modules 等目录
|
|
317
|
+
if (['node_modules'].includes(entry.name)) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
await addFilesToArchive(fullPath, false, entryRelativePath);
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
if (root) {
|
|
325
|
+
// 可以配置忽略指定文件名的文件
|
|
326
|
+
if ([''].includes(entry.name)) {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// 保持与之前相同的文件添加方式,包括相对路径处理
|
|
331
|
+
archive.file(fullPath, { name: entryRelativePath });
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
await addFilesToArchive(path_1.default.resolve(cwd), true);
|
|
336
|
+
await archive.finalize();
|
|
337
|
+
return bufferPromise;
|
|
338
|
+
}
|
|
339
|
+
function upload({ action, file, method = 'POST', headers = {}, withCredentials = false }) {
|
|
340
|
+
return (0, utils_1.fetchStream)(action, {
|
|
341
|
+
method,
|
|
342
|
+
headers,
|
|
343
|
+
body: file,
|
|
344
|
+
credentials: withCredentials ? 'include' : 'same-origin'
|
|
345
|
+
})
|
|
346
|
+
.then(async (response) => {
|
|
347
|
+
const text = await response.text();
|
|
348
|
+
const dataMeta = {
|
|
349
|
+
data: null,
|
|
350
|
+
context: {
|
|
351
|
+
response,
|
|
352
|
+
file
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
try {
|
|
356
|
+
dataMeta.data = JSON.parse(text);
|
|
357
|
+
}
|
|
358
|
+
catch (e) {
|
|
359
|
+
// If parsing fails, keep data as null
|
|
360
|
+
}
|
|
361
|
+
if (!response.ok) {
|
|
362
|
+
throw new Error(`${response.status} ${response.statusText} HTTP ERROR`);
|
|
363
|
+
}
|
|
364
|
+
return dataMeta;
|
|
365
|
+
})
|
|
366
|
+
.catch(error => {
|
|
367
|
+
throw error; // 或者可以在这里进行错误转换
|
|
368
|
+
});
|
|
369
|
+
}
|
package/lib/utils/index.js
CHANGED
|
@@ -91,29 +91,39 @@ async function downloadAndExtractRemoteZip(downloadUrl, targetPath) {
|
|
|
91
91
|
let fileCount = 0;
|
|
92
92
|
let extractedCount = 0;
|
|
93
93
|
downloadResponse
|
|
94
|
-
.body.pipe(unzipper_1.default.Parse())
|
|
94
|
+
.body.pipe(unzipper_1.default.Parse())
|
|
95
95
|
.on('error', reject)
|
|
96
|
-
.on('entry', entry => {
|
|
97
|
-
fileCount++;
|
|
96
|
+
.on('entry', async (entry) => {
|
|
98
97
|
const filePath = path_1.default.join(dirPath, entry.path);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (extractedCount === fileCount) {
|
|
107
|
-
resolve('');
|
|
98
|
+
try {
|
|
99
|
+
// 确保父目录存在(处理嵌套目录情况)
|
|
100
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(filePath));
|
|
101
|
+
// 如果是目录则创建,否则写入文件
|
|
102
|
+
if (entry.type === 'Directory') {
|
|
103
|
+
await fs_extra_1.default.ensureDir(filePath);
|
|
104
|
+
extractedCount++;
|
|
108
105
|
}
|
|
109
|
-
|
|
106
|
+
else {
|
|
107
|
+
fileCount++;
|
|
108
|
+
entry
|
|
109
|
+
.pipe(fs_extra_1.default.createWriteStream(filePath))
|
|
110
|
+
.on('error', reject)
|
|
111
|
+
.on('finish', () => {
|
|
112
|
+
extractedCount++;
|
|
113
|
+
checkCompletion();
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
reject(err);
|
|
119
|
+
}
|
|
110
120
|
})
|
|
111
|
-
.on('close',
|
|
112
|
-
|
|
113
|
-
if (fileCount === extractedCount) {
|
|
121
|
+
.on('close', checkCompletion);
|
|
122
|
+
function checkCompletion() {
|
|
123
|
+
if (fileCount > 0 && fileCount === extractedCount) {
|
|
114
124
|
resolve('');
|
|
115
125
|
}
|
|
116
|
-
}
|
|
126
|
+
}
|
|
117
127
|
});
|
|
118
128
|
}
|
|
119
129
|
function getRuntime() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Environment } from '../environment';
|
|
2
2
|
import { IResponseInfo } from '../interfaces';
|
|
3
|
-
import { CloudrunServerType, ICloudrunDetailResponse, ICloudrunListResponse, ITemplate } from './type';
|
|
3
|
+
import { CloudrunServerType, ICloudrunDetailResponse, ICloudrunListResponse, ICloudrunServerBaseConfig, ITemplate } from './type';
|
|
4
4
|
/**
|
|
5
5
|
* 云托管服务管理类
|
|
6
6
|
* 提供云托管服务的初始化、下载、列表查询和删除等功能
|
|
@@ -73,9 +73,39 @@ export declare class CloudRunService {
|
|
|
73
73
|
delete(params: {
|
|
74
74
|
serverName: string;
|
|
75
75
|
}): Promise<IResponseInfo>;
|
|
76
|
+
/**
|
|
77
|
+
* 本地代码部署云托管服务
|
|
78
|
+
* @param {Object} params 部署参数
|
|
79
|
+
* @param {string} params.serverName 要部署的服务名称
|
|
80
|
+
* @param {string} params.targetPath 本地代码路径
|
|
81
|
+
* @param {Object} [params.serverConfig] 服务配置项(可选)
|
|
82
|
+
* @param {string[]} [params.serverConfig.OpenAccessTypes] 开放访问类型
|
|
83
|
+
* @param {number} [params.serverConfig.Cpu] CPU规格
|
|
84
|
+
* @param {number} [params.serverConfig.Mem] 内存规格
|
|
85
|
+
* @param {number} [params.serverConfig.MinNum] 最小实例数
|
|
86
|
+
* @param {number} [params.serverConfig.MaxNum] 最大实例数
|
|
87
|
+
* @param {Object} [params.serverConfig.PolicyDetails] 策略详情
|
|
88
|
+
* @param {Object} [params.serverConfig.CustomLogs] 自定义日志配置
|
|
89
|
+
* @param {Object} [params.serverConfig.EnvParams] 环境变量参数
|
|
90
|
+
* @param {number} [params.serverConfig.Port] 端口(函数型服务不允许设置)
|
|
91
|
+
* @param {string} [params.serverConfig.Dockerfile] Dockerfile路径
|
|
92
|
+
* @param {string} [params.serverConfig.BuildDir] 构建目录
|
|
93
|
+
* @param {boolean} [params.serverConfig.InternalAccess] 是否开启内网访问
|
|
94
|
+
* @param {string} [params.serverConfig.InternalDomain] 内网域名
|
|
95
|
+
* @param {string} [params.serverConfig.EntryPoint] 入口文件
|
|
96
|
+
* @param {string} [params.serverConfig.Cmd] 启动命令
|
|
97
|
+
* @returns {Promise<IResponseInfo>} 返回部署操作的响应信息
|
|
98
|
+
*/
|
|
99
|
+
deploy(params: {
|
|
100
|
+
serverName: string;
|
|
101
|
+
targetPath: string;
|
|
102
|
+
serverConfig?: Partial<Pick<ICloudrunServerBaseConfig, 'OpenAccessTypes' | 'Cpu' | 'Mem' | 'MinNum' | 'MaxNum' | 'PolicyDetails' | 'CustomLogs' | 'EnvParams' | 'Port' | 'Dockerfile' | 'BuildDir' | 'InternalAccess' | 'InternalDomain' | 'EntryPoint' | 'Cmd'>>;
|
|
103
|
+
}): Promise<IResponseInfo>;
|
|
76
104
|
/**
|
|
77
105
|
* 获取云托管服务模板列表
|
|
78
106
|
* @returns {Promise<ITemplate[]>} 返回模板数组
|
|
79
107
|
*/
|
|
80
108
|
getTemplates(): Promise<ITemplate[]>;
|
|
109
|
+
private _checkFunctionExist;
|
|
110
|
+
private _upsertFunction;
|
|
81
111
|
}
|