@abtnode/docker-utils 1.16.38-beta-20250108-235929-17742885
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/LICENSE +13 -0
- package/dist/index.cjs +656 -0
- package/dist/index.d.cts +82 -0
- package/dist/index.d.mts +82 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.mjs +636 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2018-2025 ArcBlock
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Joi = require('joi');
|
|
4
|
+
|
|
5
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
6
|
+
|
|
7
|
+
const Joi__default = /*#__PURE__*/_interopDefaultCompat(Joi);
|
|
8
|
+
|
|
9
|
+
const whiteDockerArgs = {
|
|
10
|
+
// 短参数映射
|
|
11
|
+
"-a": "--attach",
|
|
12
|
+
// 将标准输入、输出或错误流附加到容器(通常与 -i -t 一起使用)
|
|
13
|
+
"-d": "--detach",
|
|
14
|
+
// 以后台模式运行容器(分离模式)
|
|
15
|
+
"-e": "--env",
|
|
16
|
+
// 设置环境变量(例如:-e "VAR=value")
|
|
17
|
+
"-h": "--hostname",
|
|
18
|
+
// 设置容器的主机名
|
|
19
|
+
"-i": "--interactive",
|
|
20
|
+
// 保持 STDIN 为打开状态(与 -t 一起用可进入交互式模式)
|
|
21
|
+
"-it": "-it",
|
|
22
|
+
"-rm": "-rm",
|
|
23
|
+
"-m": "--memory",
|
|
24
|
+
// 限制容器可使用的内存大小(例如:-m 512m)
|
|
25
|
+
"-p": "--publish",
|
|
26
|
+
// 将容器的端口映射到主机(例如:-p 8080:80)
|
|
27
|
+
"-P": "--publish-all",
|
|
28
|
+
// 将容器暴露的所有端口随机映射到主机端口
|
|
29
|
+
"-l": "--label",
|
|
30
|
+
// 为容器添加自定义元数据标签(例如:-l "key=value")
|
|
31
|
+
"-n": "--name",
|
|
32
|
+
// 为容器指定名称,而不是自动分配
|
|
33
|
+
"-q": "--quiet",
|
|
34
|
+
// 静默模式,不输出日志信息(通常用于 docker build)
|
|
35
|
+
"-x": "--x-registry",
|
|
36
|
+
// 非标准参数(此处为自定义场景)可用于指定镜像注册表等
|
|
37
|
+
"-t": "--tty",
|
|
38
|
+
// 分配一个伪 TTY(终端),常与 -i 一起使用
|
|
39
|
+
"-u": "--user",
|
|
40
|
+
// 指定以哪个用户或 UID 运行容器内的进程
|
|
41
|
+
"-v": "--volume",
|
|
42
|
+
// 挂载宿主机目录或数据卷到容器中
|
|
43
|
+
"-w": "--workdir",
|
|
44
|
+
// 设置容器内的工作目录
|
|
45
|
+
"-cgroupns": "--cgroupns",
|
|
46
|
+
// 设置 cgroup 命名空间的模式(host 或 private)
|
|
47
|
+
"-cpus": "--cpus",
|
|
48
|
+
// 限制可用给容器的 CPU 数量
|
|
49
|
+
"-g": "--gpus",
|
|
50
|
+
// 分配 GPU 给容器(需要适配 NVIDIA runtime)
|
|
51
|
+
"-ip6": "--ip6",
|
|
52
|
+
// 设置容器的 IPv6 地址
|
|
53
|
+
"--read-only": "--read-only",
|
|
54
|
+
// 将容器的文件系统设置为只读
|
|
55
|
+
// 长参数映射
|
|
56
|
+
"--add-host": "--add-host",
|
|
57
|
+
// 添加自定义主机名映射到 /etc/hosts 中(格式:host:IP)
|
|
58
|
+
"--attach": "--attach",
|
|
59
|
+
// 附加到容器的 STDIN/STDOUT/STDERR
|
|
60
|
+
"--blkio-weight": "--blkio-weight",
|
|
61
|
+
// 设置容器块 IO 相对权重(需要 cgroup 支持)
|
|
62
|
+
"--blkio-weight-device": "--blkio-weight-device",
|
|
63
|
+
// 针对指定设备设置块 IO 权重
|
|
64
|
+
"--cap-add": "--cap-add",
|
|
65
|
+
// 向容器添加 Linux 功能(Capabilities)
|
|
66
|
+
"--cap-drop": "--cap-drop",
|
|
67
|
+
// 从容器中移除特定 Linux 功能
|
|
68
|
+
"--cgroup-parent": "--cgroup-parent",
|
|
69
|
+
// 指定容器的 cgroup 父组
|
|
70
|
+
"--cidfile": "--cidfile",
|
|
71
|
+
// 将创建的容器 ID 写入到指定文件中
|
|
72
|
+
"--cpu-period": "--cpu-period",
|
|
73
|
+
// 限制 CPU CFS 调度周期
|
|
74
|
+
"--cpu-quota": "--cpu-quota",
|
|
75
|
+
// 限制 CPU CFS 配额,控制 CPU 时间的使用量
|
|
76
|
+
"--cpu-rt-period": "--cpu-rt-period",
|
|
77
|
+
// 调整 CPU 实时调度周期(需要特定权限)
|
|
78
|
+
"--cpu-rt-runtime": "--cpu-rt-runtime",
|
|
79
|
+
// 调整 CPU 实时调度运行时间(需要特定权限)
|
|
80
|
+
"--cpu-shares": "--cpu-shares",
|
|
81
|
+
// 设置 CPU 共享权重(相对值)
|
|
82
|
+
"--cpus": "--cpus",
|
|
83
|
+
// 限制容器可用 CPU 数量(例如:--cpus 2.5)
|
|
84
|
+
"--cpuset-cpus": "--cpuset-cpus",
|
|
85
|
+
// 指定容器能使用的 CPU 核心(例如:--cpuset-cpus="0-2,4")
|
|
86
|
+
"--cpuset-mems": "--cpuset-mems",
|
|
87
|
+
// 指定容器能使用的内存节点(NUMA 节点)
|
|
88
|
+
"--detach": "--detach",
|
|
89
|
+
// 后台运行容器
|
|
90
|
+
"--detach-keys": "--detach-keys",
|
|
91
|
+
// 设置分离容器的键组合
|
|
92
|
+
"--device": "--device",
|
|
93
|
+
// 将主机设备映射到容器(例如:--device=/dev/sda:/dev/xvda)
|
|
94
|
+
"--device-cgroup-rule": "--device-cgroup-rule",
|
|
95
|
+
// 为设备访问添加 cgroup 规则
|
|
96
|
+
"--device-read-bps": "--device-read-bps",
|
|
97
|
+
// 限制设备读取速率(字节/秒)
|
|
98
|
+
"--device-read-iops": "--device-read-iops",
|
|
99
|
+
// 限制设备读取 IOPS
|
|
100
|
+
"--device-write-bps": "--device-write-bps",
|
|
101
|
+
// 限制设备写入速率(字节/秒)
|
|
102
|
+
"--device-write-iops": "--device-write-iops",
|
|
103
|
+
// 限制设备写入 IOPS
|
|
104
|
+
"--disable-content-trust": "--disable-content-trust",
|
|
105
|
+
// 禁用镜像拉取时的内容信任校验
|
|
106
|
+
"--dns": "--dns",
|
|
107
|
+
// 设置容器使用的 DNS 服务器
|
|
108
|
+
"--dns-opt": "--dns-opt",
|
|
109
|
+
// 为 DNS 配置添加选项
|
|
110
|
+
"--dns-search": "--dns-search",
|
|
111
|
+
// 为 DNS 搜索域配置添加域名
|
|
112
|
+
"--domainname": "--domainname",
|
|
113
|
+
// 设置容器的域名(FQDN)
|
|
114
|
+
"--entrypoint": "--entrypoint",
|
|
115
|
+
// 覆盖镜像的默认 ENTRYPOINT
|
|
116
|
+
"--env": "--env",
|
|
117
|
+
// 设置环境变量
|
|
118
|
+
"--env-file": "--env-file",
|
|
119
|
+
// 从文件中读取环境变量
|
|
120
|
+
"--expose": "--expose",
|
|
121
|
+
// 声明容器将会监听的端口(不做主机端口映射,只是元数据声明)
|
|
122
|
+
"--gpus": "--gpus",
|
|
123
|
+
// 分配 GPU 给容器
|
|
124
|
+
"--group-add": "--group-add",
|
|
125
|
+
// 向容器添加更多用户组(GID)
|
|
126
|
+
"--health-cmd": "--health-cmd",
|
|
127
|
+
// 容器健康检查命令
|
|
128
|
+
"--health-interval": "--health-interval",
|
|
129
|
+
// 健康检查的时间间隔
|
|
130
|
+
"--health-retries": "--health-retries",
|
|
131
|
+
// 健康检查的重试次数
|
|
132
|
+
"--health-start-period": "--health-start-period",
|
|
133
|
+
// 健康检查开始前的初始化等待时间
|
|
134
|
+
"--health-timeout": "--health-timeout",
|
|
135
|
+
// 健康检查的超时时间
|
|
136
|
+
"--help": "--help",
|
|
137
|
+
// 显示帮助信息
|
|
138
|
+
"--hostname": "--hostname",
|
|
139
|
+
// 设置容器主机名
|
|
140
|
+
"--init": "--init",
|
|
141
|
+
// 在容器内使用 init 进程(帮助处理僵尸进程)
|
|
142
|
+
"--interactive": "--interactive",
|
|
143
|
+
// 保持 STDIN 打开
|
|
144
|
+
"--ip": "--ip",
|
|
145
|
+
// 指定容器的 IPv4 地址(需自定义网络)
|
|
146
|
+
"--ip6": "--ip6",
|
|
147
|
+
// 指定容器的 IPv6 地址(需自定义网络)
|
|
148
|
+
"--ipc": "--ipc",
|
|
149
|
+
// 设置容器的 IPC 命名空间(shareable, container:id, 或 host)
|
|
150
|
+
"--isolation": "--isolation",
|
|
151
|
+
// 指定容器隔离级别(Windows 环境下使用,如 process 或 hyperv)
|
|
152
|
+
"--kernel-memory": "--kernel-memory",
|
|
153
|
+
// 限制内核内存使用(已弃用)
|
|
154
|
+
"--label": "--label",
|
|
155
|
+
// 为容器添加自定义元数据标签
|
|
156
|
+
"--label-file": "--label-file",
|
|
157
|
+
// 从文件中读取标签
|
|
158
|
+
"--link": "--link",
|
|
159
|
+
// 连接到另一个容器的网络别名(已弃用,建议用网络替代)
|
|
160
|
+
"--link-local-ip": "--link-local-ip",
|
|
161
|
+
// 为容器分配一个链接本地地址(IPv4/IPv6)
|
|
162
|
+
"--log-driver": "--log-driver",
|
|
163
|
+
// 设置容器日志驱动(例如 json-file、syslog 等)
|
|
164
|
+
"--log-opt": "--log-opt",
|
|
165
|
+
// 为日志驱动添加额外配置选项
|
|
166
|
+
"--mac-address": "--mac-address",
|
|
167
|
+
// 设置容器的 MAC 地址(需自定义网络)
|
|
168
|
+
"--memory": "--memory",
|
|
169
|
+
// 限制容器可使用的内存
|
|
170
|
+
"--memory-reservation": "--memory-reservation",
|
|
171
|
+
// 设置内存软限制(触发内存管理)
|
|
172
|
+
"--memory-swap": "--memory-swap",
|
|
173
|
+
// 设置内存+swap 的限制值
|
|
174
|
+
"--memory-swappiness": "--memory-swappiness",
|
|
175
|
+
// 调整内存交换行为(0-100)
|
|
176
|
+
"--mount": "--mount",
|
|
177
|
+
// 使用高级语法挂载数据卷或绑定宿主机目录
|
|
178
|
+
"--name": "--name",
|
|
179
|
+
// 指定容器名称
|
|
180
|
+
"--network": "--network",
|
|
181
|
+
// 指定容器使用的网络(默认 bridge)
|
|
182
|
+
"--network-alias": "--network-alias",
|
|
183
|
+
// 为容器添加网络别名(在用户定义网络中)
|
|
184
|
+
"--no-healthcheck": "--no-healthcheck",
|
|
185
|
+
// 禁用继承自镜像的健康检查
|
|
186
|
+
"--oom-kill-disable": "--oom-kill-disable",
|
|
187
|
+
// 禁用内存不足时对容器进程的杀死操作
|
|
188
|
+
"--oom-score-adj": "--oom-score-adj",
|
|
189
|
+
// 调整容器的 OOM 分数(影响 OOM 杀死优先级)
|
|
190
|
+
"--pid": "--pid",
|
|
191
|
+
// 设置 PID 命名空间(容器与主机共享进程视图)
|
|
192
|
+
"--pids-limit": "--pids-limit",
|
|
193
|
+
// 限制容器内可用的进程数
|
|
194
|
+
"--platform": "--platform",
|
|
195
|
+
// 指定镜像平台(如 linux/amd64)
|
|
196
|
+
"--privileged": "--privileged",
|
|
197
|
+
// 提升容器为特权模式(访问更多主机资源)
|
|
198
|
+
"--publish": "--publish",
|
|
199
|
+
// 映射容器端口到宿主机
|
|
200
|
+
"--publish-all": "--publish-all",
|
|
201
|
+
// 自动映射容器暴露的所有端口到宿主机随机端口
|
|
202
|
+
"--pull": "--pull",
|
|
203
|
+
// 指定拉取镜像策略(always, missing, never)
|
|
204
|
+
"--restart": "--restart",
|
|
205
|
+
// 容器退出后的重启策略(no, on-failure, always, unless-stopped)
|
|
206
|
+
"--rm": "--rm",
|
|
207
|
+
// 容器停止后自动删除容器
|
|
208
|
+
"--runtime": "--runtime",
|
|
209
|
+
// 指定运行时(如 runc, nvidia-container-runtime)
|
|
210
|
+
"--security-opt": "--security-opt",
|
|
211
|
+
// 设置安全选项(如 seccomp, apparmor 配置)
|
|
212
|
+
"--shm-size": "--shm-size",
|
|
213
|
+
// 设置 /dev/shm 的大小(默认 64M)
|
|
214
|
+
"--stop-signal": "--stop-signal",
|
|
215
|
+
// 设置停止容器时发送的信号(默认 SIGTERM)
|
|
216
|
+
"--stop-timeout": "--stop-timeout",
|
|
217
|
+
// 停止容器时等待指定秒数后发送 SIGKILL
|
|
218
|
+
"--storage-opt": "--storage-opt",
|
|
219
|
+
// 设置存储驱动的选项(如 devicemapper)
|
|
220
|
+
"--sysctl": "--sysctl",
|
|
221
|
+
// 设置内核参数(sysctl)
|
|
222
|
+
"--tmpfs": "--tmpfs",
|
|
223
|
+
// 挂载一个 tmpfs 临时文件系统
|
|
224
|
+
"--tty": "--tty",
|
|
225
|
+
// 分配一个伪终端 TTY
|
|
226
|
+
"--ulimit": "--ulimit",
|
|
227
|
+
// 设置容器的 ulimit 限制(如打开文件数)
|
|
228
|
+
"--user": "--user",
|
|
229
|
+
// 指定容器内运行进程的用户/组
|
|
230
|
+
"--userns": "--userns",
|
|
231
|
+
// 设置用户命名空间模式(host 或 private)
|
|
232
|
+
"--uts": "--uts",
|
|
233
|
+
// 设置 UTS 命名空间(host 或 container)
|
|
234
|
+
"--volume": "--volume",
|
|
235
|
+
// 挂载宿主机目录或数据卷到容器中
|
|
236
|
+
"--volume-driver": "--volume-driver",
|
|
237
|
+
// 指定数据卷驱动
|
|
238
|
+
"--volumes-from": "--volumes-from",
|
|
239
|
+
// 从另一个容器挂载数据卷
|
|
240
|
+
"--workdir": "--workdir"
|
|
241
|
+
// 设置容器内的工作目录
|
|
242
|
+
};
|
|
243
|
+
const allowDockerArgs = {
|
|
244
|
+
"--env": true,
|
|
245
|
+
"--publish": true,
|
|
246
|
+
"--volume": true,
|
|
247
|
+
"--attach": true,
|
|
248
|
+
"--quiet": true,
|
|
249
|
+
// '--user': true,
|
|
250
|
+
"--workdir": true,
|
|
251
|
+
"--cidfile": true,
|
|
252
|
+
"--detach-keys": true,
|
|
253
|
+
"--disable-content-trust": true,
|
|
254
|
+
"--domainname": true,
|
|
255
|
+
"--expose": true,
|
|
256
|
+
"--ip": true,
|
|
257
|
+
"--link-local-ip": true,
|
|
258
|
+
"--platform": true
|
|
259
|
+
};
|
|
260
|
+
const autoSetDockerArgs = {
|
|
261
|
+
"-rm": true,
|
|
262
|
+
"--name": true,
|
|
263
|
+
"--label": true,
|
|
264
|
+
"--network": true,
|
|
265
|
+
"--user": true
|
|
266
|
+
};
|
|
267
|
+
const notAllowUseCustomDockerArgs = {
|
|
268
|
+
"--volume": true,
|
|
269
|
+
"--publish": true
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const needValueKeys = /* @__PURE__ */ new Set(["--publish", "--volume"]);
|
|
273
|
+
function addBlockletPrefixVolume(args) {
|
|
274
|
+
return args.map((arg) => {
|
|
275
|
+
if (arg.key === "--volume" && !arg.value.startsWith("$BLOCKLET_APP_DIR/") && !arg.value.startsWith("$BLOCKLET_DATA_DIR/")) {
|
|
276
|
+
return {
|
|
277
|
+
...arg,
|
|
278
|
+
value: arg.value.startsWith("/") ? `$BLOCKLET_DATA_DIR${arg.value}` : `$BLOCKLET_DATA_DIR/${arg.value}`
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
return arg;
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
function dockerParseCommand(command) {
|
|
285
|
+
if (!command) {
|
|
286
|
+
return { dockerArgs: [], dockerEnvs: [], dockerImage: "" };
|
|
287
|
+
}
|
|
288
|
+
command = command.trim();
|
|
289
|
+
if (command.startsWith("$ docker run")) {
|
|
290
|
+
command = command.replace("$ docker run", "docker run");
|
|
291
|
+
}
|
|
292
|
+
if (!command.startsWith("docker run")) {
|
|
293
|
+
return { dockerArgs: [], dockerEnvs: [], dockerImage: "" };
|
|
294
|
+
}
|
|
295
|
+
const cleanedCommand = command.replace(/\\\n/g, "").replace(/\n/g, " ").trim();
|
|
296
|
+
const parts = cleanedCommand.match(/(?:[^\s"']+|'[^']*'|"[^"]*")+/g);
|
|
297
|
+
if (!parts) {
|
|
298
|
+
return { dockerArgs: [], dockerEnvs: [], dockerImage: "" };
|
|
299
|
+
}
|
|
300
|
+
const dockerArgs = [];
|
|
301
|
+
const dockerEnvs = [];
|
|
302
|
+
let i = 0;
|
|
303
|
+
let dockerImage = "";
|
|
304
|
+
while (i < parts.length) {
|
|
305
|
+
const part = parts[i];
|
|
306
|
+
if (!part) {
|
|
307
|
+
i++;
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
if (part === "docker" || part === "run") {
|
|
311
|
+
i++;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if ((part === "-e" || part === "--env") && i + 1 < parts.length) {
|
|
315
|
+
const envString = parts[i + 1] || "";
|
|
316
|
+
const [envKey, ...envValueParts] = envString.split("=");
|
|
317
|
+
if (envKey) {
|
|
318
|
+
const envValue = envValueParts.join("=").replace(/^['"]|['"]$/g, "");
|
|
319
|
+
dockerEnvs.push({ key: envKey, value: envValue });
|
|
320
|
+
}
|
|
321
|
+
i += 2;
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if (part.startsWith("-")) {
|
|
325
|
+
const equalIndex = part.indexOf("=");
|
|
326
|
+
let rawKey = part;
|
|
327
|
+
let value = null;
|
|
328
|
+
if (equalIndex !== -1) {
|
|
329
|
+
rawKey = part.substring(0, equalIndex);
|
|
330
|
+
value = part.substring(equalIndex + 1).replace(/^['"]|['"]$/g, "");
|
|
331
|
+
}
|
|
332
|
+
const fullKey = whiteDockerArgs[rawKey];
|
|
333
|
+
if (!fullKey) {
|
|
334
|
+
i++;
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (fullKey === "--publish") {
|
|
338
|
+
let publishValue = "";
|
|
339
|
+
if (value) {
|
|
340
|
+
publishValue = value;
|
|
341
|
+
dockerArgs.push({ key: fullKey, value: publishValue });
|
|
342
|
+
} else {
|
|
343
|
+
if (i + 1 < parts.length && !(parts[i + 1] || "").startsWith("-")) {
|
|
344
|
+
const nextVal = (parts[i + 1] || "").replace(/^['"]|['"]$/g, "");
|
|
345
|
+
publishValue = nextVal;
|
|
346
|
+
dockerArgs.push({ key: fullKey, value: publishValue });
|
|
347
|
+
i += 2;
|
|
348
|
+
continue;
|
|
349
|
+
} else {
|
|
350
|
+
dockerArgs.push({ key: fullKey, value: "true" });
|
|
351
|
+
i++;
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
i++;
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (value !== null) {
|
|
359
|
+
dockerArgs.push({ key: fullKey, value });
|
|
360
|
+
i++;
|
|
361
|
+
continue;
|
|
362
|
+
} else {
|
|
363
|
+
if (i + 1 < parts.length && !(parts[i + 1] || "").startsWith("-")) {
|
|
364
|
+
const nextVal = (parts[i + 1] || "").replace(/^['"]|['"]$/g, "");
|
|
365
|
+
dockerArgs.push({ key: fullKey, value: nextVal });
|
|
366
|
+
i += 2;
|
|
367
|
+
continue;
|
|
368
|
+
} else {
|
|
369
|
+
dockerArgs.push({ key: fullKey, value: "true" });
|
|
370
|
+
i++;
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
dockerImage = part.replace(/^['"]|['"]$/g, "");
|
|
376
|
+
i++;
|
|
377
|
+
}
|
|
378
|
+
const filteredDockerArgs = [];
|
|
379
|
+
for (const arg of dockerArgs) {
|
|
380
|
+
arg.name = "";
|
|
381
|
+
arg.path = "";
|
|
382
|
+
arg.prefix = "";
|
|
383
|
+
arg.protocol = "";
|
|
384
|
+
arg.type = "docker";
|
|
385
|
+
if (needValueKeys.has(arg.key) && arg.value === "true") {
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
filteredDockerArgs.push(arg);
|
|
389
|
+
}
|
|
390
|
+
for (const item of dockerEnvs) {
|
|
391
|
+
item.custom = "";
|
|
392
|
+
item.description = "";
|
|
393
|
+
item.secure = false;
|
|
394
|
+
item.shared = false;
|
|
395
|
+
item.required = false;
|
|
396
|
+
}
|
|
397
|
+
return { dockerArgs: addBlockletPrefixVolume(filteredDockerArgs), dockerEnvs, dockerImage };
|
|
398
|
+
}
|
|
399
|
+
function dockerBuildCommand({
|
|
400
|
+
dockerArgs,
|
|
401
|
+
dockerEnvs,
|
|
402
|
+
dockerImage
|
|
403
|
+
}) {
|
|
404
|
+
if (dockerArgs.length === 0 && dockerEnvs.length === 0 && !dockerImage) {
|
|
405
|
+
return "";
|
|
406
|
+
}
|
|
407
|
+
const parts = ["docker run \\"];
|
|
408
|
+
const multipleArgs = /* @__PURE__ */ new Set([
|
|
409
|
+
"--publish",
|
|
410
|
+
"--volume",
|
|
411
|
+
"--expose",
|
|
412
|
+
"--device",
|
|
413
|
+
"--mount",
|
|
414
|
+
"--label",
|
|
415
|
+
"--cap-add",
|
|
416
|
+
"--cap-drop",
|
|
417
|
+
"--add-host",
|
|
418
|
+
"--dns",
|
|
419
|
+
"--network-alias",
|
|
420
|
+
"--sysctl",
|
|
421
|
+
"--ulimit",
|
|
422
|
+
"--network"
|
|
423
|
+
]);
|
|
424
|
+
for (const arg of dockerArgs) {
|
|
425
|
+
const { key, value } = arg;
|
|
426
|
+
if (multipleArgs.has(key)) {
|
|
427
|
+
if (value === "true") {
|
|
428
|
+
parts.push(` ${key} \\`);
|
|
429
|
+
} else {
|
|
430
|
+
const formattedValue = /[\s"']/g.test(value) ? `"${value}"` : value;
|
|
431
|
+
parts.push(` ${key} ${formattedValue} \\`);
|
|
432
|
+
}
|
|
433
|
+
} else {
|
|
434
|
+
if (!parts.some((part) => part.startsWith(` ${key}`))) {
|
|
435
|
+
if (value === "true") {
|
|
436
|
+
parts.push(` ${key} \\`);
|
|
437
|
+
} else {
|
|
438
|
+
const formattedValue = /[\s"']/g.test(value) ? `"${value}"` : value;
|
|
439
|
+
parts.push(` ${key} ${formattedValue} \\`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
for (const env of dockerEnvs) {
|
|
445
|
+
const { key, value } = env;
|
|
446
|
+
const formattedEnvValue = /["']/g.test(value) ? value : `"${value}"`;
|
|
447
|
+
parts.push(` -e ${key}=${formattedEnvValue} \\`);
|
|
448
|
+
}
|
|
449
|
+
if (dockerImage) {
|
|
450
|
+
parts.push(` ${dockerImage}`);
|
|
451
|
+
} else {
|
|
452
|
+
if (parts[parts.length - 1]?.endsWith("\\")) {
|
|
453
|
+
parts[parts.length - 1] = (parts[parts.length - 1] || "").slice(0, -1).trim();
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return parts.join("\n");
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const dockerArgsValidator = {
|
|
460
|
+
"--volume": (volume) => {
|
|
461
|
+
if (!volume.startsWith("$BLOCKLET_APP_DIR/") && !volume.startsWith("$BLOCKLET_DATA_DIR/")) {
|
|
462
|
+
return "Volume must start with $BLOCKLET_APP_DIR or $BLOCKLET_DATA_DIR";
|
|
463
|
+
}
|
|
464
|
+
if (volume.indexOf("..") > -1) {
|
|
465
|
+
return 'Volume cannot contain ".."';
|
|
466
|
+
}
|
|
467
|
+
return "";
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
const dockerArgsToCamelCase = (str) => {
|
|
472
|
+
const strippedStr = str.replace(/^[-]+/, "");
|
|
473
|
+
const camelCaseStr = strippedStr.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
474
|
+
return camelCaseStr;
|
|
475
|
+
};
|
|
476
|
+
const dockerCamelCaseToDash = (str) => {
|
|
477
|
+
const kebabCaseStr = str.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
478
|
+
return `--${kebabCaseStr}`;
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
const allowedDockerArgKeys = Array.from(new Set(Object.keys(allowDockerArgs).map(dockerArgsToCamelCase)));
|
|
482
|
+
const multiValueArgs = ["publish", "volume"];
|
|
483
|
+
const dockerSchema = Joi__default.object({
|
|
484
|
+
workdir: Joi__default.string().trim().optional(),
|
|
485
|
+
image: Joi__default.string().trim().regex(/^[^\s]+$/).optional(),
|
|
486
|
+
shell: Joi__default.string().trim().optional(),
|
|
487
|
+
// network: Joi.string().valid('host', 'bridge', 'none').default('host').optional(),
|
|
488
|
+
volumes: Joi__default.array().items(Joi__default.string().trim()).optional(),
|
|
489
|
+
script: Joi__default.string().trim().optional(),
|
|
490
|
+
installNodeModules: Joi__default.boolean().optional(),
|
|
491
|
+
command: Joi__default.string().trim().optional(),
|
|
492
|
+
runBaseScript: Joi__default.boolean().optional(),
|
|
493
|
+
// 动态 Docker 参数
|
|
494
|
+
...allowedDockerArgKeys.reduce(
|
|
495
|
+
(acc, key) => {
|
|
496
|
+
const validFn = dockerArgsValidator[dockerCamelCaseToDash(key)];
|
|
497
|
+
if (multiValueArgs.includes(key)) {
|
|
498
|
+
acc[key] = Joi__default.array().items(Joi__default.string().trim()).optional();
|
|
499
|
+
if (validFn) {
|
|
500
|
+
acc[key] = Joi__default.array().items(
|
|
501
|
+
Joi__default.string().trim().custom((value, helper) => {
|
|
502
|
+
const msg = validFn(value);
|
|
503
|
+
if (msg) {
|
|
504
|
+
return helper.message(msg);
|
|
505
|
+
}
|
|
506
|
+
return value;
|
|
507
|
+
})
|
|
508
|
+
// eslint-disable-line @typescript-eslint/comma-dangle
|
|
509
|
+
).optional();
|
|
510
|
+
} else {
|
|
511
|
+
acc[key] = Joi__default.array().items(Joi__default.string().trim()).optional();
|
|
512
|
+
}
|
|
513
|
+
return acc;
|
|
514
|
+
}
|
|
515
|
+
if (validFn) {
|
|
516
|
+
acc[key] = Joi__default.string().trim().custom((value, helper) => {
|
|
517
|
+
const msg = validFn(value);
|
|
518
|
+
if (msg) {
|
|
519
|
+
return helper.message(msg);
|
|
520
|
+
}
|
|
521
|
+
return value;
|
|
522
|
+
}).optional();
|
|
523
|
+
} else {
|
|
524
|
+
acc[key] = Joi__default.string().trim().optional();
|
|
525
|
+
}
|
|
526
|
+
return acc;
|
|
527
|
+
},
|
|
528
|
+
{}
|
|
529
|
+
// eslint-disable-line @typescript-eslint/comma-dangle
|
|
530
|
+
)
|
|
531
|
+
}).optional();
|
|
532
|
+
const validateDockerImage = (dockerImage) => {
|
|
533
|
+
const reg = /^(?:[a-zA-Z0-9.-]+(?::[0-9]+)?\/)?(?:[a-z0-9]+(?:[._-][a-z0-9]+)*\/)?[a-z0-9]+(?:[._-][a-z0-9]+)*(:[a-zA-Z0-9_.-]+)?$/;
|
|
534
|
+
return Joi__default.string().trim().regex(reg).required().validate(dockerImage);
|
|
535
|
+
};
|
|
536
|
+
const parseDockerArgsToSchema = (dockerImage, args) => {
|
|
537
|
+
const obj = {};
|
|
538
|
+
args.forEach((item) => {
|
|
539
|
+
const key = dockerArgsToCamelCase(item.key);
|
|
540
|
+
if (multiValueArgs.includes(key)) {
|
|
541
|
+
obj[key] = [...obj[key] || [], item.value];
|
|
542
|
+
} else {
|
|
543
|
+
obj[key] = item.value;
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
const { error } = validateDockerImage(dockerImage);
|
|
547
|
+
if (error) {
|
|
548
|
+
return { error, value: obj };
|
|
549
|
+
}
|
|
550
|
+
obj.image = dockerImage;
|
|
551
|
+
return dockerSchema.validate(obj);
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
function dockerParsePublishPorts(dockerImage, dockerArgs) {
|
|
555
|
+
const updatedDockerArgs = JSON.parse(JSON.stringify(dockerArgs));
|
|
556
|
+
const hostPortMap = /* @__PURE__ */ new Map();
|
|
557
|
+
let blockletPortIndex = 1;
|
|
558
|
+
const publishPorts = [];
|
|
559
|
+
updatedDockerArgs.forEach((arg) => {
|
|
560
|
+
if (arg.key === "--publish") {
|
|
561
|
+
const publishValue = arg.value;
|
|
562
|
+
const parts = publishValue.split(":");
|
|
563
|
+
let hostIP = "127.0.0.1";
|
|
564
|
+
let hostPort = "";
|
|
565
|
+
let containerPort = "";
|
|
566
|
+
if (parts.length === 2) {
|
|
567
|
+
[hostPort, containerPort] = parts;
|
|
568
|
+
} else if (parts.length === 3) {
|
|
569
|
+
[hostIP, hostPort, containerPort] = parts;
|
|
570
|
+
} else if (parts.length === 1) {
|
|
571
|
+
containerPort = parts[0] || "";
|
|
572
|
+
}
|
|
573
|
+
let blockletPort = hostPortMap.get(hostPort || "");
|
|
574
|
+
if (!blockletPort) {
|
|
575
|
+
blockletPort = `$BLOCKLET_PORT_${blockletPortIndex++}`;
|
|
576
|
+
hostPortMap.set(hostPort || "", blockletPort);
|
|
577
|
+
}
|
|
578
|
+
if (!publishPorts.find((port) => port.port === hostPort)) {
|
|
579
|
+
publishPorts.push({
|
|
580
|
+
type: publishPorts.length === 0 ? "web" : "docker",
|
|
581
|
+
name: `${dockerImage}-${publishPorts.length + 1}`,
|
|
582
|
+
path: `/${dockerImage}-${publishPorts.length + 1}`,
|
|
583
|
+
port: hostPort || containerPort,
|
|
584
|
+
// 若未指定 hostPort,则使用 containerPort
|
|
585
|
+
containerPort: Number(containerPort) || 0,
|
|
586
|
+
hostIP
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
return publishPorts;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function dockerParseEnvironments(envs, ignoreCustom = false) {
|
|
595
|
+
const blockletEnvs = [];
|
|
596
|
+
if (!envs) {
|
|
597
|
+
return blockletEnvs;
|
|
598
|
+
}
|
|
599
|
+
for (const env of envs) {
|
|
600
|
+
const item = {
|
|
601
|
+
name: env.key,
|
|
602
|
+
default: env.value === `$${env.key}` ? "" : env.value,
|
|
603
|
+
secure: env.secure !== void 0 ? env.secure : false,
|
|
604
|
+
shared: env.shared !== void 0 ? env.shared : false,
|
|
605
|
+
required: env.required !== void 0 ? env.required : false,
|
|
606
|
+
description: env.description || env.key
|
|
607
|
+
};
|
|
608
|
+
if (!ignoreCustom) {
|
|
609
|
+
item.custom = env.custom || "";
|
|
610
|
+
}
|
|
611
|
+
blockletEnvs.push(item);
|
|
612
|
+
}
|
|
613
|
+
return blockletEnvs;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function getObjByPath(data, path) {
|
|
617
|
+
const obj = JSON.parse(JSON.stringify(data));
|
|
618
|
+
const keys = path.split(".").map((key) => {
|
|
619
|
+
const match = key.match(/(.+)\[(.+?)\]/);
|
|
620
|
+
return match ? [match[1], match[2]] : key;
|
|
621
|
+
}).flat();
|
|
622
|
+
return keys.filter(Boolean).reduce((acc, key) => {
|
|
623
|
+
if (!acc || !key) {
|
|
624
|
+
return void 0;
|
|
625
|
+
}
|
|
626
|
+
const conditionMatch = key?.match(/(.+?)=(.+)/);
|
|
627
|
+
if (Array.isArray(acc) && conditionMatch) {
|
|
628
|
+
const [, field, value] = conditionMatch;
|
|
629
|
+
if (field && value) {
|
|
630
|
+
acc = acc.find((item) => {
|
|
631
|
+
const lowerField = Object.keys(item).find((k) => k.toLowerCase() === field.toLowerCase());
|
|
632
|
+
return lowerField && item[lowerField] === value;
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
return acc;
|
|
636
|
+
}
|
|
637
|
+
const lowerKey = Object.keys(acc).find((k) => k.toLowerCase() === key.toLowerCase());
|
|
638
|
+
return lowerKey ? acc[lowerKey] : void 0;
|
|
639
|
+
}, obj);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
exports.allowDockerArgs = allowDockerArgs;
|
|
643
|
+
exports.autoSetDockerArgs = autoSetDockerArgs;
|
|
644
|
+
exports.dockerArgsToCamelCase = dockerArgsToCamelCase;
|
|
645
|
+
exports.dockerArgsValidator = dockerArgsValidator;
|
|
646
|
+
exports.dockerBuildCommand = dockerBuildCommand;
|
|
647
|
+
exports.dockerCamelCaseToDash = dockerCamelCaseToDash;
|
|
648
|
+
exports.dockerParseCommand = dockerParseCommand;
|
|
649
|
+
exports.dockerParseEnvironments = dockerParseEnvironments;
|
|
650
|
+
exports.dockerParsePublishPorts = dockerParsePublishPorts;
|
|
651
|
+
exports.dockerSchema = dockerSchema;
|
|
652
|
+
exports.getObjByPath = getObjByPath;
|
|
653
|
+
exports.multiValueArgs = multiValueArgs;
|
|
654
|
+
exports.notAllowUseCustomDockerArgs = notAllowUseCustomDockerArgs;
|
|
655
|
+
exports.parseDockerArgsToSchema = parseDockerArgsToSchema;
|
|
656
|
+
exports.whiteDockerArgs = whiteDockerArgs;
|