diff --git a/src/main/java/de/hftstuttgart/dtabackend/utils/DockerUtil.java b/src/main/java/de/hftstuttgart/dtabackend/utils/DockerUtil.java index 194388b0e164fba6451e029b3c03b1ada973b603..57872f1a77afc7fa722ec0061fd574bd1ee161fc 100644 --- a/src/main/java/de/hftstuttgart/dtabackend/utils/DockerUtil.java +++ b/src/main/java/de/hftstuttgart/dtabackend/utils/DockerUtil.java @@ -25,6 +25,10 @@ import java.util.Arrays; import java.util.UUID; import java.util.stream.Collectors; +import java.nio.file.Path; +import java.nio.file.Paths; + + @Component public class DockerUtil { private static final Logger LOG = LogManager.getLogger(DockerUtil.class); @@ -46,95 +50,97 @@ public class DockerUtil { dockerClient = DockerClientImpl.getInstance(dockerClientConfig, httpClient); } - public int runContainer(String image, Bind... binds) throws InterruptedException, IOException { - LOG.debug(String.format("pull image: %s", image)); - try { - dockerClient.pullImageCmd(image) - .start() - .awaitCompletion() - .close(); - } catch (DockerException e) { - LOG.error(String.format( - "pulling docker image %s failed with %s, trying with local image", - image, - e.getMessage())); - } - - LOG.debug("creating container"); - CreateContainerResponse containerResponse; - try { - // Extract paths from binds - String testPath = null; - String srcPath = null; - String resultPath = null; - for (Bind bind : binds) { - Volume volume = bind.getVolume(); - if (volume.getPath().contains("test")) { - testPath = bind.getPath(); - } else if (volume.getPath().contains("src")) { - srcPath = bind.getPath(); - } else if (volume.getPath().contains("result")) { - resultPath = bind.getPath(); - } - } - - if (testPath == null || srcPath == null || resultPath == null) { - throw new IllegalArgumentException("All required paths (testPath, srcPath, resultPath) must be provided."); - } - - containerResponse = dockerClient.createContainerCmd("testcontainer") - .withImage(image) - .withHostConfig( - HostConfig.newHostConfig() - .withBinds(binds) - ) - .withCmd( - "java", - "-Djava.security.egd=file:/dev/./urandom", - "-jar", - "/data/app.jar", - String.format("%s:%s", srcPath, testPath), - "/data/libs/*:/data/test/libs/*", - resultPath - ) - .exec(); - } catch (DockerException e) { - LOG.error(String.format( - "Creating Docker Testrunner container failed with %s", e.getMessage())); - throw e; - } - - LOG.debug(String.format("container created: %s", containerResponse.getId())); - - LOG.debug(String.format("starting container %s", containerResponse.getId())); - dockerClient.startContainerCmd(containerResponse.getId()).exec(); - - LOG.debug(String.format("retrieving logs for container %s", containerResponse.getId())); - dockerClient.logContainerCmd(containerResponse.getId()) - .withStdOut(true) - .withStdErr(true) - .withFollowStream(true) - .exec(new ResultCallback.Adapter<Frame>() { - @Override - public void onNext(Frame frame) { - // Process logs here - LOG.debug(String.format("LOG: %s", new String(frame.getPayload(), StandardCharsets.UTF_8))); - } - }); - - LOG.debug(String.format("waiting for completion of container %s", containerResponse.getId())); - int ret = dockerClient - .waitContainerCmd(containerResponse.getId()) + public int runContainer(String image, Path testPath, Path srcPath, Path resultPath) + throws InterruptedException, IOException { + LOG.debug(String.format("Pulling image: %s", image)); + try { + dockerClient.pullImageCmd(image) .start() .awaitCompletion() - .awaitStatusCode(); - LOG.debug(String.format("container completed with status %d", ret)); - - LOG.debug(String.format("deleting container %s", containerResponse.getId())); - dockerClient.removeContainerCmd(containerResponse.getId()) - .withRemoveVolumes(true) + .close(); + } catch (DockerException e) { + LOG.error(String.format( + "Pulling Docker image %s failed with %s, trying with local image", + image, e.getMessage())); + } + + LOG.debug("Creating container"); + CreateContainerResponse containerResponse; + try { + // Validate paths are absolute + String absTestPath = ensureAbsolutePath(testPath); + String absSrcPath = ensureAbsolutePath(srcPath); + String absResultPath = ensureAbsolutePath(resultPath); + + // Debugging the paths + LOG.debug(String.format( + "Volume bindings: %s -> /data/test, %s -> /data/src, %s -> /data/result", + absTestPath, absSrcPath, absResultPath)); + + // Create the container + containerResponse = dockerClient.createContainerCmd("testcontainer") + .withImage(image) + .withHostConfig( + HostConfig.newHostConfig() + .withBinds( + new Bind(absTestPath, new Volume("/data/test")), + new Bind(absSrcPath, new Volume("/data/src")), + new Bind(absResultPath, new Volume("/data/result")) + ) + ) + .withCmd( + "java", + "-Djava.security.egd=file:/dev/./urandom", + "-jar", + "/data/app.jar", + "/data/src:/data/test", + "/data/libs/*:/data/test/libs/*", + "/data/result" + ) .exec(); - - return ret; - } + } catch (DockerException e) { + LOG.error(String.format("Creating Docker Testrunner container failed with %s", e.getMessage())); + throw e; + } + + LOG.debug(String.format("Container created: %s", containerResponse.getId())); + + LOG.debug(String.format("Starting container %s", containerResponse.getId())); + dockerClient.startContainerCmd(containerResponse.getId()).exec(); + + LOG.debug(String.format("Retrieving logs for container %s", containerResponse.getId())); + dockerClient.logContainerCmd(containerResponse.getId()) + .withStdOut(true) + .withStdErr(true) + .withFollowStream(true) + .exec(new ResultCallback.Adapter<Frame>() { + @Override + public void onNext(Frame frame) { + LOG.debug(String.format("LOG: %s", new String(frame.getPayload(), StandardCharsets.UTF_8))); + } + }); + + LOG.debug(String.format("Waiting for completion of container %s", containerResponse.getId())); + int ret = dockerClient + .waitContainerCmd(containerResponse.getId()) + .start() + .awaitCompletion() + .awaitStatusCode(); + LOG.debug(String.format("Container completed with status %d", ret)); + + LOG.debug(String.format("Deleting container %s", containerResponse.getId())); + dockerClient.removeContainerCmd(containerResponse.getId()) + .withRemoveVolumes(true) + .exec(); + + return ret; +} + +private String ensureAbsolutePath(Path path) { + if (!path.isAbsolute()) { + throw new IllegalArgumentException("Path must be absolute: " + path.toString()); + } + return path.toString(); +} + } diff --git a/src/main/java/de/hftstuttgart/dtabackend/utils/ExecuteTestUtil.java b/src/main/java/de/hftstuttgart/dtabackend/utils/ExecuteTestUtil.java index cacb6b9521bd04c535fdbbf6f7e9d4c29e5346f5..0bd635bce35057d4613838aab6ef9bf33ef44efe 100644 --- a/src/main/java/de/hftstuttgart/dtabackend/utils/ExecuteTestUtil.java +++ b/src/main/java/de/hftstuttgart/dtabackend/utils/ExecuteTestUtil.java @@ -95,19 +95,21 @@ public class ExecuteTestUtil { image = config.group(5); } + Path testCodePath = Paths.get(testPath.toString(), "test"); + Path srcCodePath = Paths.get(srcPath.toString(), "src"); + // Log the paths before binding LOG.debug("Binding paths for Docker container:"); - LOG.debug("Test Path: {}", testPath); - LOG.debug("Source Path: {}", srcPath); + LOG.debug("Test Path: {}", testCodePath); + LOG.debug("Source Path: {}", srcCodePath); LOG.debug("Result Path: {}", resultPath); - // Start the test-container with professor-given image and submission-specific volume mounts + // Start the test-container with professor-given image and specific paths dockerUtil.runContainer( - image, - new Bind(testPath.toString(), new Volume("test")), - new Bind(srcPath.toString(), new Volume("src")), - new Bind(resultPath.toString(), new Volume("result")) - ); + image, + testCodePath, + srcCodePath, + resultPath); return generateResult(assignmentId, resultPath, testPath); }