Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added custom spotless config #28

Merged
merged 3 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions JShellAPI/src/main/java/org/togetherjava/jshellapi/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,11 @@
import org.springframework.lang.Nullable;

@ConfigurationProperties("jshellapi")
public record Config(
long regularSessionTimeoutSeconds,
long oneTimeSessionTimeoutSeconds,
long evalTimeoutSeconds,
long evalTimeoutValidationLeeway,
int sysOutCharLimit,
long maxAliveSessions,
int dockerMaxRamMegaBytes,
double dockerCPUsUsage,
@Nullable String dockerCPUSetCPUs,
long schedulerSessionKillScanRateSeconds,
long dockerResponseTimeout,
long dockerConnectionTimeout) {
public record Config(long regularSessionTimeoutSeconds, long oneTimeSessionTimeoutSeconds,
long evalTimeoutSeconds, long evalTimeoutValidationLeeway, int sysOutCharLimit,
long maxAliveSessions, int dockerMaxRamMegaBytes, double dockerCPUsUsage,
@Nullable String dockerCPUSetCPUs, long schedulerSessionKillScanRateSeconds,
long dockerResponseTimeout, long dockerConnectionTimeout) {
public Config {
if (regularSessionTimeoutSeconds <= 0)
throw new IllegalArgumentException("Invalid value " + regularSessionTimeoutSeconds);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package org.togetherjava.jshellapi.dto;

public record JShellEvalAbortion(
String sourceCause, String remainingSource, JShellEvalAbortionCause cause) {}
public record JShellEvalAbortion(String sourceCause, String remainingSource,
JShellEvalAbortionCause cause) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@
public sealed interface JShellEvalAbortionCause {

@JsonTypeName("TIMEOUT")
record TimeoutAbortionCause() implements JShellEvalAbortionCause {}
record TimeoutAbortionCause() implements JShellEvalAbortionCause {
}

@JsonTypeName("UNCAUGHT_EXCEPTION")
record UnhandledExceptionAbortionCause(String exceptionClass, String exceptionMessage)
implements JShellEvalAbortionCause {}
record UnhandledExceptionAbortionCause(String exceptionClass,
String exceptionMessage) implements JShellEvalAbortionCause {
}

@JsonTypeName("COMPILE_TIME_ERROR")
record CompileTimeErrorAbortionCause(List<String> errors) implements JShellEvalAbortionCause {}
record CompileTimeErrorAbortionCause(List<String> errors) implements JShellEvalAbortionCause {
}

@JsonTypeName("SYNTAX_ERROR")
record SyntaxErrorAbortionCause() implements JShellEvalAbortionCause {}
record SyntaxErrorAbortionCause() implements JShellEvalAbortionCause {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@

import java.util.List;

public record JShellResult(
List<JShellSnippetResult> snippetsResults,
@Nullable JShellEvalAbortion abortion,
boolean stdoutOverflow,
String stdout) {
public record JShellResult(List<JShellSnippetResult> snippetsResults,
@Nullable JShellEvalAbortion abortion, boolean stdoutOverflow, String stdout) {
public JShellResult {
snippetsResults = List.copyOf(snippetsResults);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
package org.togetherjava.jshellapi.dto;

public record JShellResultWithId(String id, JShellResult result) {}
public record JShellResultWithId(String id, JShellResult result) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

import org.springframework.lang.Nullable;

public record JShellSnippetResult(
SnippetStatus status, SnippetType type, int id, String source, @Nullable String result) {}
public record JShellSnippetResult(SnippetStatus status, SnippetType type, int id, String source,
@Nullable String result) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ public DockerException(Throwable cause) {
super(cause);
}

public DockerException(
String message,
Throwable cause,
boolean enableSuppression,
public DockerException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;

import org.togetherjava.jshellapi.dto.JShellResult;
import org.togetherjava.jshellapi.dto.JShellResultWithId;
import org.togetherjava.jshellapi.exceptions.DockerException;
Expand All @@ -21,64 +22,45 @@ public class JShellController {
private StartupScriptsService startupScriptsService;

@PostMapping("/eval/{id}")
public JShellResult eval(
@PathVariable String id,
public JShellResult eval(@PathVariable String id,
@RequestParam(required = false) StartupScriptId startupScriptId,
@RequestBody String code)
throws DockerException {
@RequestBody String code) throws DockerException {
validateId(id);
return service.session(id, startupScriptId)
.eval(code)
.orElseThrow(
() ->
new ResponseStatusException(
HttpStatus.CONFLICT, "An operation is already running"));
.eval(code)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.CONFLICT,
"An operation is already running"));
}

@PostMapping("/eval")
public JShellResultWithId eval(
@RequestParam(required = false) StartupScriptId startupScriptId,
@RequestBody String code)
throws DockerException {
public JShellResultWithId eval(@RequestParam(required = false) StartupScriptId startupScriptId,
@RequestBody String code) throws DockerException {
JShellService jShellService = service.session(startupScriptId);
return new JShellResultWithId(
jShellService.id(),
jShellService
.eval(code)
.orElseThrow(
() ->
new ResponseStatusException(
HttpStatus.CONFLICT,
"An operation is already running")));
return new JShellResultWithId(jShellService.id(),
jShellService.eval(code)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.CONFLICT,
"An operation is already running")));
}

@PostMapping("/single-eval")
public JShellResult singleEval(
@RequestParam(required = false) StartupScriptId startupScriptId,
@RequestBody String code)
throws DockerException {
public JShellResult singleEval(@RequestParam(required = false) StartupScriptId startupScriptId,
@RequestBody String code) throws DockerException {
JShellService jShellService = service.oneTimeSession(startupScriptId);
return jShellService
.eval(code)
.orElseThrow(
() ->
new ResponseStatusException(
HttpStatus.CONFLICT, "An operation is already running"));
return jShellService.eval(code)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.CONFLICT,
"An operation is already running"));
}

@GetMapping("/snippets/{id}")
public List<String> snippets(
@PathVariable String id, @RequestParam(required = false) boolean includeStartupScript)
throws DockerException {
public List<String> snippets(@PathVariable String id,
@RequestParam(required = false) boolean includeStartupScript) throws DockerException {
validateId(id);
if (!service.hasSession(id))
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Id " + id + " not found");
return service.session(id, null)
.snippets(includeStartupScript)
.orElseThrow(
() ->
new ResponseStatusException(
HttpStatus.CONFLICT, "An operation is already running"));
.snippets(includeStartupScript)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.CONFLICT,
"An operation is already running"));
}

@DeleteMapping("/{id}")
Expand Down Expand Up @@ -106,8 +88,7 @@ public void setStartupScriptsService(StartupScriptsService startupScriptsService

private static void validateId(String id) throws ResponseStatusException {
if (!id.matches("[a-zA-Z0-9][a-zA-Z0-9_.-]+")) {
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST,
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Id " + id + " doesn't match the regex [a-zA-Z0-9][a-zA-Z0-9_.-]+");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;

import org.togetherjava.jshellapi.Config;

import java.io.*;
Expand All @@ -33,20 +33,20 @@ public DockerService(Config config) {
DefaultDockerClientConfig clientConfig =
DefaultDockerClientConfig.createDefaultConfigBuilder().build();
ApacheDockerHttpClient httpClient =
new ApacheDockerHttpClient.Builder()
.dockerHost(clientConfig.getDockerHost())
.sslConfig(clientConfig.getSSLConfig())
.responseTimeout(Duration.ofSeconds(config.dockerResponseTimeout()))
.connectionTimeout(Duration.ofSeconds(config.dockerConnectionTimeout()))
.build();
new ApacheDockerHttpClient.Builder().dockerHost(clientConfig.getDockerHost())
.sslConfig(clientConfig.getSSLConfig())
.responseTimeout(Duration.ofSeconds(config.dockerResponseTimeout()))
.connectionTimeout(Duration.ofSeconds(config.dockerConnectionTimeout()))
.build();
this.client = DockerClientImpl.getInstance(clientConfig, httpClient);

cleanupLeftovers(WORKER_UNIQUE_ID);
}

private void cleanupLeftovers(UUID currentId) {
for (Container container :
client.listContainersCmd().withLabelFilter(Set.of(WORKER_LABEL)).exec()) {
for (Container container : client.listContainersCmd()
.withLabelFilter(Set.of(WORKER_LABEL))
.exec()) {
String containerHumanName =
container.getId() + " " + Arrays.toString(container.getNames());
LOGGER.info("Found worker container '{}'", containerHumanName);
Expand All @@ -57,50 +57,44 @@ private void cleanupLeftovers(UUID currentId) {
}
}

public String spawnContainer(
long maxMemoryMegs,
long cpus,
@Nullable String cpuSetCpus,
String name,
Duration evalTimeout,
long sysoutLimit)
throws InterruptedException {
public String spawnContainer(long maxMemoryMegs, long cpus, @Nullable String cpuSetCpus,
String name, Duration evalTimeout, long sysoutLimit) throws InterruptedException {
String imageName = "togetherjava.org:5001/togetherjava/jshellwrapper";
boolean presentLocally =
client.listImagesCmd().withFilter("reference", List.of(imageName)).exec().stream()
.flatMap(it -> Arrays.stream(it.getRepoTags()))
.anyMatch(it -> it.endsWith(":master"));
boolean presentLocally = client.listImagesCmd()
.withFilter("reference", List.of(imageName))
.exec()
.stream()
.flatMap(it -> Arrays.stream(it.getRepoTags()))
.anyMatch(it -> it.endsWith(":master"));

if (!presentLocally) {
client.pullImageCmd(imageName)
.withTag("master")
.exec(new PullImageResultCallback())
.awaitCompletion(5, TimeUnit.MINUTES);
.withTag("master")
.exec(new PullImageResultCallback())
.awaitCompletion(5, TimeUnit.MINUTES);
}

return client.createContainerCmd(imageName + ":master")
.withHostConfig(
HostConfig.newHostConfig()
.withAutoRemove(true)
.withInit(true)
.withCapDrop(Capability.ALL)
.withNetworkMode("none")
.withPidsLimit(2000L)
.withReadonlyRootfs(true)
.withMemory(maxMemoryMegs * 1024 * 1024)
.withCpuCount(cpus)
.withCpusetCpus(cpuSetCpus))
.withStdinOpen(true)
.withAttachStdin(true)
.withAttachStderr(true)
.withAttachStdout(true)
.withEnv(
"evalTimeoutSeconds=" + evalTimeout.toSeconds(),
"sysOutCharLimit=" + sysoutLimit)
.withLabels(Map.of(WORKER_LABEL, WORKER_UNIQUE_ID.toString()))
.withName(name)
.exec()
.getId();
.withHostConfig(HostConfig.newHostConfig()
.withAutoRemove(true)
.withInit(true)
.withCapDrop(Capability.ALL)
.withNetworkMode("none")
.withPidsLimit(2000L)
.withReadonlyRootfs(true)
.withMemory(maxMemoryMegs * 1024 * 1024)
.withCpuCount(cpus)
.withCpusetCpus(cpuSetCpus))
.withStdinOpen(true)
.withAttachStdin(true)
.withAttachStderr(true)
.withAttachStdout(true)
.withEnv("evalTimeoutSeconds=" + evalTimeout.toSeconds(),
"sysOutCharLimit=" + sysoutLimit)
.withLabels(Map.of(WORKER_LABEL, WORKER_UNIQUE_ID.toString()))
.withName(name)
.exec()
.getId();
}

public InputStream startAndAttachToContainer(String containerId, InputStream stdin)
Expand All @@ -109,31 +103,28 @@ public InputStream startAndAttachToContainer(String containerId, InputStream std
PipedOutputStream pipeOut = new PipedOutputStream(pipeIn);

client.attachContainerCmd(containerId)
.withLogs(true)
.withFollowStream(true)
.withStdOut(true)
.withStdErr(true)
.withStdIn(stdin)
.exec(
new ResultCallback.Adapter<>() {
@Override
public void onNext(Frame object) {
try {
String payloadString =
new String(object.getPayload(), StandardCharsets.UTF_8);
if (object.getStreamType() == StreamType.STDOUT) {
pipeOut.write(object.getPayload());
} else {
LOGGER.warn(
"Received STDERR from container {}: {}",
containerId,
payloadString);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
});
.withLogs(true)
.withFollowStream(true)
.withStdOut(true)
.withStdErr(true)
.withStdIn(stdin)
.exec(new ResultCallback.Adapter<>() {
@Override
public void onNext(Frame object) {
try {
String payloadString =
new String(object.getPayload(), StandardCharsets.UTF_8);
if (object.getStreamType() == StreamType.STDOUT) {
pipeOut.write(object.getPayload());
} else {
LOGGER.warn("Received STDERR from container {}: {}", containerId,
payloadString);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
});

client.startContainerCmd(containerId).exec();
return pipeIn;
Expand Down
Loading
Loading