From cbee3797feba63fb4288bc29ba7fdad493fbf316 Mon Sep 17 00:00:00 2001
From: opensnail <598092184@qq.com>
Date: Sat, 14 Dec 2024 12:51:40 +0800
Subject: [PATCH] =?UTF-8?q?feat:(unify):=20=E5=AE=8C=E6=88=90=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=E7=9A=84=E6=8B=89=E5=8E=BB=E5=92=8C=E7=BC=96=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
snail-job-server/pom.xml | 12 ++
snail-job-server/snail-job-server-web/pom.xml | 9 ++
.../web/controller/DockerController.java | 44 ++++++
.../web/controller/FileViewerController.java | 52 +++++--
.../web/controller/PythonController.java | 17 +++
.../base/SjBuildImageResultCallback.java | 72 ++++++++++
.../server/web/model/enums/FileTypeEnum.java | 14 ++
.../web/model/request/RunPythonRequestVO.java | 11 ++
.../web/model/response/ContainerVO.java | 31 +++++
.../server/web/model/response/FileVO.java | 14 ++
.../web/model/response/SaveFileRequestVO.java | 2 -
.../server/web/service/DockerService.java | 21 +++
.../server/web/service/PythonService.java | 9 ++
.../web/service/handler/DockerHandler.java | 37 +++++
.../web/service/impl/DockerServiceImpl.java | 131 ++++++++++++++++++
.../web/service/impl/PythonServiceImpl.java | 77 ++++++++++
16 files changed, 537 insertions(+), 16 deletions(-)
create mode 100644 snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/controller/DockerController.java
create mode 100644 snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/controller/PythonController.java
create mode 100644 snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/base/SjBuildImageResultCallback.java
create mode 100644 snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/enums/FileTypeEnum.java
create mode 100644 snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/request/RunPythonRequestVO.java
create mode 100644 snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/response/ContainerVO.java
create mode 100644 snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/response/FileVO.java
create mode 100644 snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/DockerService.java
create mode 100644 snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/PythonService.java
create mode 100644 snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/handler/DockerHandler.java
create mode 100644 snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/impl/DockerServiceImpl.java
create mode 100644 snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/impl/PythonServiceImpl.java
diff --git a/snail-job-server/pom.xml b/snail-job-server/pom.xml
index ce286592a..f5bf892f3 100644
--- a/snail-job-server/pom.xml
+++ b/snail-job-server/pom.xml
@@ -31,6 +31,7 @@
4.4.0
0.9.16
33.2.0-jre
+ 3.4.0
@@ -120,6 +121,17 @@
perf4j
${perf4j.version}
+
+ com.github.docker-java
+ docker-java
+ ${docker-java.version}
+
+
+
+ com.github.docker-java
+ docker-java-transport-okhttp
+ ${docker-java.version}
+
diff --git a/snail-job-server/snail-job-server-web/pom.xml b/snail-job-server/snail-job-server-web/pom.xml
index 746d45758..ac3011434 100644
--- a/snail-job-server/snail-job-server-web/pom.xml
+++ b/snail-job-server/snail-job-server-web/pom.xml
@@ -95,6 +95,15 @@
cn.hutool
hutool-crypto
+
+ com.github.docker-java
+ docker-java
+
+
+
+ com.github.docker-java
+ docker-java-transport-okhttp
+
diff --git a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/controller/DockerController.java b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/controller/DockerController.java
new file mode 100644
index 000000000..15b86114f
--- /dev/null
+++ b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/controller/DockerController.java
@@ -0,0 +1,44 @@
+package com.aizuda.snailjob.server.web.controller;
+
+import com.aizuda.snailjob.server.web.model.response.ContainerVO;
+import com.aizuda.snailjob.server.web.service.DockerService;
+import com.github.dockerjava.api.async.ResultCallback;
+import com.github.dockerjava.api.command.InspectContainerResponse;
+import com.github.dockerjava.api.model.Frame;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/docker")
+@RequiredArgsConstructor
+public class DockerController {
+
+ private final DockerService dockerService;
+
+ @GetMapping("/container/list")
+ public List getContainerList(@RequestParam("containerName") String containerName) {
+ return dockerService.getContainerList(containerName);
+ }
+
+ @GetMapping("/container/{id}")
+ public InspectContainerResponse getContainerById(@PathVariable("id") String id) {
+ return dockerService.getContainerById(id);
+ }
+
+ @GetMapping("/container/log/{id}")
+ public ResultCallback getContainerLogById(@PathVariable("id") String id) throws InterruptedException {
+ return dockerService.getContainerLogById(id);
+ }
+
+ @GetMapping("/create/container")
+ public boolean createContainer(@RequestParam("containerName") String containerName, @RequestParam("imageName") String imageName) {
+ return dockerService.createContainer(containerName, imageName);
+ }
+
+ @GetMapping("/build/image")
+ public boolean buildImage(@RequestParam("imagerName") String imagerName, @RequestParam("path") String path) {
+ return dockerService.buildImage(imagerName, path);
+ }
+}
diff --git a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/controller/FileViewerController.java b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/controller/FileViewerController.java
index 6cab264ad..1d9c1a8b5 100644
--- a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/controller/FileViewerController.java
+++ b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/controller/FileViewerController.java
@@ -1,41 +1,70 @@
package com.aizuda.snailjob.server.web.controller;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.StrUtil;
import com.aizuda.snailjob.server.common.exception.SnailJobServerException;
+import com.aizuda.snailjob.server.web.model.enums.FileTypeEnum;
+import com.aizuda.snailjob.server.web.model.response.FileVO;
import com.aizuda.snailjob.server.web.model.response.SaveFileRequestVO;
import com.aizuda.snailjob.server.web.model.response.ViewFileResponseVO;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
+import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.Comparator;
import java.util.List;
+import java.util.stream.Collectors;
@RestController
@RequestMapping("/file")
@RequiredArgsConstructor
public class FileViewerController {
+ // todo 用户配置或者默认是运行jar的所在目录
+ private static final String workdir = "/Users/zhangshuguang/";
// 显示文件列表
@GetMapping("/files")
- public List listFiles(@RequestParam("rootPath") String rootPath) {
+ public List listFiles(@RequestParam(value = "directory") String directory) {
try {
// 读取目录下的文件列表
- return Files.list(Paths.get(rootPath))
- .map(path -> path.getFileName().toString())
- .toList();
+ return Files.list(Paths.get(workdir + StrUtil.SLASH + directory))
+ .map(path ->{
+ File file = path.toFile();
+ FileVO fileVO = new FileVO();
+ fileVO.setFileName(file.getName());
+// fileVO.setFilePath(file.getPath());
+ if (file.isDirectory()) {
+ fileVO.setFileType(FileTypeEnum.DIRECTORY.getType());
+ } else {
+ fileVO.setFileType(FileTypeEnum.FILE.getType());
+ }
+ return fileVO;
+ })
+ .toList().stream().sorted(new Comparator() {
+ @Override
+ public int compare(FileVO o1, FileVO o2) {
+ if (o2.getFileType().compareTo(o1.getFileType()) == 0) {
+ return o1.getFileName().compareTo(o2.getFileName());
+ }
+ return o2.getFileType().compareTo(o1.getFileType());
+ }
+ }).collect(Collectors.toList());
} catch (IOException e) {
throw new SnailJobServerException("获取文件列表失败", e);
}
}
// 显示文件内容
- @GetMapping("/file")
- public ViewFileResponseVO viewFile(@RequestParam("fileName") String fileName, @RequestParam("fullPath") String fullPath) {
+ @GetMapping("/detail")
+ public ViewFileResponseVO viewFile(@RequestParam(value = "directory") String directory) {
try {
// 构建文件路径
- String filePath = Paths.get(fullPath, fileName).toString();
+ String filePath = Paths.get(workdir + StrUtil.SLASH + directory).toString();
// 读取文件内容
String s = Files.readString(Paths.get(filePath));
@@ -48,14 +77,9 @@ public class FileViewerController {
}
// 保存修改后的文件内容
- @PostMapping("/file")
+ @PutMapping
public Boolean saveFile(@RequestBody SaveFileRequestVO requestVO) {
- try {
- String filePath = Paths.get(requestVO.getFilePath(), requestVO.getFileName()).toString();
- Files.writeString(Paths.get(filePath), requestVO.getContent()); // 更新文件内容
+ FileUtil.writeUtf8String(requestVO.getContent().trim(), workdir + StrUtil.SLASH + requestVO.getFileName());
return true;
- } catch (IOException e) {
- throw new SnailJobServerException("文件更新时候", e);
- }
}
}
diff --git a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/controller/PythonController.java b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/controller/PythonController.java
new file mode 100644
index 000000000..885fcd4ad
--- /dev/null
+++ b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/controller/PythonController.java
@@ -0,0 +1,17 @@
+package com.aizuda.snailjob.server.web.controller;
+
+import com.aizuda.snailjob.server.web.model.request.RunPythonRequestVO;
+import com.aizuda.snailjob.server.web.service.PythonService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/python")
+@RequiredArgsConstructor
+public class PythonController {
+ private final PythonService pythonService;
+ @PostMapping("/run")
+ public Boolean run(@RequestBody RunPythonRequestVO requestVO) {
+ return pythonService.runPython(requestVO);
+ }
+}
diff --git a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/base/SjBuildImageResultCallback.java b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/base/SjBuildImageResultCallback.java
new file mode 100644
index 000000000..74028bd09
--- /dev/null
+++ b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/base/SjBuildImageResultCallback.java
@@ -0,0 +1,72 @@
+package com.aizuda.snailjob.server.web.model.base;
+
+import com.github.dockerjava.api.async.ResultCallbackTemplate;
+import com.github.dockerjava.api.exception.DockerClientException;
+import com.github.dockerjava.api.model.BuildResponseItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.TimeUnit;
+
+public class SjBuildImageResultCallback extends ResultCallbackTemplate {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(com.github.dockerjava.api.command.BuildImageResultCallback.class);
+
+ private String imageId;
+
+ private String error;
+
+ @Override
+ public void onNext(BuildResponseItem item) {
+ if (item.isBuildSuccessIndicated()) {
+ this.imageId = item.getImageId();
+ } else if (item.isErrorIndicated()) {
+ this.error = item.getError();
+ }
+ LOGGER.info("{}", item.getStream());
+ }
+
+ /**
+ * Awaits the image id from the response stream.
+ *
+ * @throws DockerClientException
+ * if the build fails.
+ */
+ public String awaitImageId() {
+ try {
+ awaitCompletion();
+ } catch (InterruptedException e) {
+ throw new DockerClientException("", e);
+ }
+
+ return getImageId();
+ }
+
+ /**
+ * Awaits the image id from the response stream.
+ *
+ * @throws DockerClientException
+ * if the build fails or the timeout occurs.
+ */
+ public String awaitImageId(long timeout, TimeUnit timeUnit) {
+ try {
+ awaitCompletion(timeout, timeUnit);
+ } catch (InterruptedException e) {
+ throw new DockerClientException("Awaiting image id interrupted: ", e);
+ }
+
+ return getImageId();
+ }
+
+ private String getImageId() {
+ if (error != null) {
+ throw new DockerClientException("Could not build image: " + error);
+ }
+
+ if (imageId != null) {
+ return imageId;
+ }
+
+ throw new DockerClientException("Could not build image");
+ }
+}
\ No newline at end of file
diff --git a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/enums/FileTypeEnum.java b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/enums/FileTypeEnum.java
new file mode 100644
index 000000000..eba738ace
--- /dev/null
+++ b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/enums/FileTypeEnum.java
@@ -0,0 +1,14 @@
+package com.aizuda.snailjob.server.web.model.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public enum FileTypeEnum {
+
+ FILE(1),
+ DIRECTORY(2);
+
+ private final int type;
+}
diff --git a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/request/RunPythonRequestVO.java b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/request/RunPythonRequestVO.java
new file mode 100644
index 000000000..132ac94b6
--- /dev/null
+++ b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/request/RunPythonRequestVO.java
@@ -0,0 +1,11 @@
+package com.aizuda.snailjob.server.web.model.request;
+
+import lombok.Data;
+
+@Data
+public class RunPythonRequestVO {
+
+ private String pythonPath;
+
+ private String command;
+}
diff --git a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/response/ContainerVO.java b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/response/ContainerVO.java
new file mode 100644
index 000000000..4068fb92a
--- /dev/null
+++ b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/response/ContainerVO.java
@@ -0,0 +1,31 @@
+package com.aizuda.snailjob.server.web.model.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class ContainerVO {
+
+ private String id;
+
+ private String name;
+
+ private String image;
+
+ private String status;
+
+ private String state;
+
+ private List ports;
+
+ @Data
+ public static class ContainerPortVO {
+ private String ip;
+ private Integer privatePort;
+ private Integer publicPort;
+ private String type;
+ }
+
+}
diff --git a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/response/FileVO.java b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/response/FileVO.java
new file mode 100644
index 000000000..dfffb55e9
--- /dev/null
+++ b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/response/FileVO.java
@@ -0,0 +1,14 @@
+package com.aizuda.snailjob.server.web.model.response;
+
+import com.aizuda.snailjob.server.web.model.enums.FileTypeEnum;
+import lombok.Data;
+
+@Data
+public class FileVO {
+
+ private String fileName;
+
+ private String filePath;
+
+ private Integer fileType;
+}
diff --git a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/response/SaveFileRequestVO.java b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/response/SaveFileRequestVO.java
index 5cc19234c..d439062d9 100644
--- a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/response/SaveFileRequestVO.java
+++ b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/model/response/SaveFileRequestVO.java
@@ -1,10 +1,8 @@
package com.aizuda.snailjob.server.web.model.response;
-import lombok.Builder;
import lombok.Data;
@Data
-@Builder
public class SaveFileRequestVO {
private String fileName;
diff --git a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/DockerService.java b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/DockerService.java
new file mode 100644
index 000000000..c21434f85
--- /dev/null
+++ b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/DockerService.java
@@ -0,0 +1,21 @@
+package com.aizuda.snailjob.server.web.service;
+
+import com.aizuda.snailjob.server.web.model.response.ContainerVO;
+import com.github.dockerjava.api.async.ResultCallback;
+import com.github.dockerjava.api.command.InspectContainerResponse;
+import com.github.dockerjava.api.model.Frame;
+
+import java.util.List;
+
+public interface DockerService {
+
+ boolean buildImage(String groupName, String path);
+
+ boolean createContainer(String containerName, String imageName);
+
+ List getContainerList(String containerName);
+
+ InspectContainerResponse getContainerById(String id);
+
+ ResultCallback getContainerLogById(String id) throws InterruptedException;
+}
diff --git a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/PythonService.java b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/PythonService.java
new file mode 100644
index 000000000..86795ccaa
--- /dev/null
+++ b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/PythonService.java
@@ -0,0 +1,9 @@
+package com.aizuda.snailjob.server.web.service;
+
+import com.aizuda.snailjob.server.web.model.request.RunPythonRequestVO;
+
+public interface PythonService {
+
+ boolean runPython(RunPythonRequestVO pythonPath);
+
+}
diff --git a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/handler/DockerHandler.java b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/handler/DockerHandler.java
new file mode 100644
index 000000000..5376aab90
--- /dev/null
+++ b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/handler/DockerHandler.java
@@ -0,0 +1,37 @@
+package com.aizuda.snailjob.server.web.service.handler;
+
+import com.github.dockerjava.api.DockerClient;
+import com.github.dockerjava.core.DefaultDockerClientConfig;
+import com.github.dockerjava.core.DockerClientBuilder;
+import com.github.dockerjava.okhttp.OkDockerHttpClient;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DockerHandler {
+
+ public DockerClient getDockerClient() {
+
+ DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
+ .withDockerTlsVerify(false)
+ // 这里填最上面填的ip端口号,ip换成服务器ip
+ .withDockerHost("unix:///var/run/docker.sock")
+ // docker API版本号,可以用docker version查看
+// .withApiVersion("1.41")
+ // 默认
+// .withRegistryUrl("https://index.docker.io/v1/")
+ .build();
+
+ OkDockerHttpClient build = new OkDockerHttpClient.Builder()
+ .dockerHost(config.getDockerHost())
+ .sslConfig(config.getSSLConfig())
+ .connectTimeout(30 * 100)
+ .build();
+
+ // Create Docker client
+ return DockerClientBuilder
+ .getInstance(config)
+ .withDockerHttpClient(build)
+ .build();
+
+ }
+}
diff --git a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/impl/DockerServiceImpl.java b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/impl/DockerServiceImpl.java
new file mode 100644
index 000000000..3a31a77a2
--- /dev/null
+++ b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/impl/DockerServiceImpl.java
@@ -0,0 +1,131 @@
+package com.aizuda.snailjob.server.web.service.impl;
+
+import com.aizuda.snailjob.common.core.util.StreamUtils;
+import com.aizuda.snailjob.server.common.util.DateUtils;
+import com.aizuda.snailjob.server.web.model.base.SjBuildImageResultCallback;
+import com.aizuda.snailjob.server.web.model.response.ContainerVO;
+import com.aizuda.snailjob.server.web.service.DockerService;
+import com.aizuda.snailjob.server.web.service.handler.DockerHandler;
+import com.github.dockerjava.api.DockerClient;
+import com.github.dockerjava.api.async.ResultCallback;
+import com.github.dockerjava.api.async.ResultCallbackTemplate;
+import com.github.dockerjava.api.command.*;
+import com.github.dockerjava.api.model.Container;
+import com.github.dockerjava.api.model.ContainerPort;
+import com.github.dockerjava.api.model.Frame;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import static com.aizuda.snailjob.server.common.util.DateUtils.PURE_DATETIME_MS_PATTERN;
+
+@Service
+@RequiredArgsConstructor
+public class DockerServiceImpl implements DockerService {
+ private final DockerHandler dockerHandler;
+
+ @Override
+ public boolean buildImage(String groupName, String path) {
+ String format = String.format("sj_%s_%s", groupName, DateUtils.toNowFormat(PURE_DATETIME_MS_PATTERN));
+ DockerClient dockerClient = dockerHandler.getDockerClient();
+ BuildImageCmd imageCmd = dockerClient
+ .buildImageCmd(new File(path))
+ .withTags(Sets.newHashSet(format));
+ imageCmd.exec(new SjBuildImageResultCallback()).awaitImageId();
+
+ return false;
+ }
+
+ @Override
+ public boolean createContainer(String containerName, String imageName) {
+ DockerClient dockerClient = dockerHandler.getDockerClient();
+ String image = String.format("%s:latest", imageName);
+ containerName = String.format("sj_%s_%s", containerName, DateUtils.toNowFormat(PURE_DATETIME_MS_PATTERN));
+
+ // Pull an image
+// try {
+// dockerClient.pullImageCmd(String.format("%s:latest", imageName)).start().awaitCompletion();
+// } catch (InterruptedException e) {
+// throw new RuntimeException(e);
+// }
+ Map labels = Maps.newHashMap();
+ labels.put("name", "snail_job");
+
+ // Create a container
+ CreateContainerResponse container = dockerClient
+ .createContainerCmd(image)
+ .withName(containerName)
+ .withLabels(labels)
+// .withCmd("echo", "Hello, Docker-Java!")
+ .exec();
+
+ // Start the container
+ dockerClient.startContainerCmd(container.getId()).exec();
+
+ return false;
+ }
+
+ @Override
+ public List getContainerList(String containerName) {
+ DockerClient dockerClient = dockerHandler.getDockerClient();
+ Map labels = Maps.newHashMap();
+ labels.put("app-name", "snail-job-server");
+ ListContainersCmd listContainersCmd = dockerClient.listContainersCmd();
+
+ List containerList = listContainersCmd.withLabelFilter(labels).withShowAll(true).exec();
+
+ return StreamUtils.toList(containerList, container -> {
+ ContainerVO containerVO = new ContainerVO();
+ containerVO.setId(container.getId());
+ containerVO.setImage(container.getImage());
+ containerVO.setName(container.getNames()[0]);
+ containerVO.setStatus(container.getStatus());
+ containerVO.setState(container.getState());
+ List portVOS = StreamUtils.toList(Arrays.stream(container.getPorts()).toList(), new Function() {
+
+ @Override
+ public ContainerVO.ContainerPortVO apply(ContainerPort containerPort) {
+ ContainerVO.ContainerPortVO portVO = new ContainerVO.ContainerPortVO();
+ portVO.setPublicPort(containerPort.getPublicPort());
+ portVO.setPrivatePort(containerPort.getPrivatePort());
+ portVO.setIp(containerPort.getIp());
+ portVO.setType(containerPort.getType());
+ return portVO;
+ }
+ });
+
+ containerVO.setPorts(portVOS);
+ return containerVO;
+ });
+ }
+
+ @Override
+ public InspectContainerResponse getContainerById(String id) {
+ DockerClient dockerClient = dockerHandler.getDockerClient();
+ return dockerClient.inspectContainerCmd(id).exec();
+ }
+
+ @Override
+ public ResultCallback getContainerLogById(String id) throws InterruptedException {
+ DockerClient dockerClient = dockerHandler.getDockerClient();
+ return dockerClient.logContainerCmd(id)
+ .withStdErr(true)
+ .withStdOut(true)
+ .withFollowStream(true)
+ .withTailAll()
+ .exec(new ResultCallbackTemplate<>() {
+ @Override
+ public void onNext(Frame object) {
+ // ws处理
+ System.out.println(object);
+ }
+ }).awaitStarted();
+ }
+}
diff --git a/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/impl/PythonServiceImpl.java b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/impl/PythonServiceImpl.java
new file mode 100644
index 000000000..f27a71a68
--- /dev/null
+++ b/snail-job-server/snail-job-server-web/src/main/java/com/aizuda/snailjob/server/web/service/impl/PythonServiceImpl.java
@@ -0,0 +1,77 @@
+package com.aizuda.snailjob.server.web.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.aizuda.snailjob.server.web.model.request.RunPythonRequestVO;
+import com.aizuda.snailjob.server.web.service.PythonService;
+import org.springframework.stereotype.Service;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+@Service
+public class PythonServiceImpl implements PythonService {
+
+ private static final String workdir = "/Users/zhangshuguang/";
+
+ @Override
+ public boolean runPython(RunPythonRequestVO requestVO) {
+ return execPython(requestVO);
+ }
+
+ public boolean execPython(RunPythonRequestVO requestVO) {
+ try {
+
+ String command = requestVO.getCommand();
+
+ String[] split = command.split("&&");
+ // Python 脚本路径
+ String pythonScriptPath = workdir + StrUtil.SLASH + requestVO.getPythonPath();
+ String pythonScriptTxt = workdir + StrUtil.SLASH + StrUtil.replaceFirst(requestVO.getPythonPath(), "main.py", "requirements.txt");
+
+ // 构建命令
+ ProcessBuilder pipBuilder = new ProcessBuilder(split[0], pythonScriptTxt);
+ pipBuilder.redirectErrorStream(true);
+
+ // 启动进程
+ Process pipProcess = pipBuilder.start();
+
+ printProcessOutput(pipProcess);
+
+ // 等待脚本执行结束
+ int pipExitCode = pipProcess.waitFor();
+ if (pipExitCode != 0) {
+ System.out.println("Script exited with code: " + pipExitCode);
+ return false;
+ }
+ // 构建命令
+ ProcessBuilder processBuilder = new ProcessBuilder(split[1], pythonScriptPath);
+ processBuilder.redirectErrorStream(true);
+
+ // 启动进程
+ Process process = processBuilder.start();
+
+ printProcessOutput(process);
+
+ // 等待脚本执行结束
+ int exitCode = process.waitFor();
+ if (exitCode != 0) {
+ System.out.println("Script exited with code: " + exitCode);
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return true;
+ }
+
+ private static void printProcessOutput(Process process) throws IOException {
+ // 获取脚本输出
+ BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ System.out.println(line);
+ }
+ }
+}