feat: build Docker entrypoint per runtime type with custom args support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -104,6 +104,28 @@ public class DeploymentExecutor {
|
||||
updateStage(deployment.id(), DeployStage.PRE_FLIGHT);
|
||||
preFlightChecks(jarPath, config);
|
||||
|
||||
// Resolve runtime type
|
||||
String resolvedRuntimeType = config.runtimeType();
|
||||
String mainClass = null;
|
||||
if ("auto".equalsIgnoreCase(resolvedRuntimeType)) {
|
||||
AppVersion appVersion = appService.getVersion(deployment.appVersionId());
|
||||
if (appVersion.detectedRuntimeType() == null) {
|
||||
throw new IllegalStateException(
|
||||
"Could not detect runtime type for JAR '" + appVersion.jarFilename() +
|
||||
"'. Set runtimeType explicitly in app configuration.");
|
||||
}
|
||||
resolvedRuntimeType = appVersion.detectedRuntimeType();
|
||||
mainClass = appVersion.detectedMainClass();
|
||||
} else if ("plain-java".equals(resolvedRuntimeType)) {
|
||||
AppVersion appVersion = appService.getVersion(deployment.appVersionId());
|
||||
mainClass = appVersion.detectedMainClass();
|
||||
if (mainClass == null) {
|
||||
throw new IllegalStateException(
|
||||
"Runtime type 'plain-java' requires a Main-Class in the JAR manifest, " +
|
||||
"but none was detected for '" + appVersion.jarFilename() + "'.");
|
||||
}
|
||||
}
|
||||
|
||||
// === PULL IMAGE ===
|
||||
updateStage(deployment.id(), DeployStage.PULL_IMAGE);
|
||||
// Docker pulls on create if not present locally
|
||||
@@ -147,7 +169,8 @@ public class DeploymentExecutor {
|
||||
config.memoryLimitBytes(), config.memoryReserveBytes(),
|
||||
config.dockerCpuShares(), config.dockerCpuQuota(),
|
||||
config.exposedPorts(), agentHealthPort,
|
||||
"on-failure", 3
|
||||
"on-failure", 3,
|
||||
resolvedRuntimeType, config.customArgs(), mainClass
|
||||
);
|
||||
|
||||
String containerId = orchestrator.startContainer(request);
|
||||
@@ -277,6 +300,8 @@ public class DeploymentExecutor {
|
||||
envVars.put("CAMELEER_AGENT_EXPORT_ENDPOINT", config.serverUrl());
|
||||
envVars.put("CAMELEER_AGENT_ROUTECONTROL_ENABLED", String.valueOf(config.routeControlEnabled()));
|
||||
envVars.put("CAMELEER_AGENT_REPLAY_ENABLED", String.valueOf(config.replayEnabled()));
|
||||
envVars.put("CAMELEER_AGENT_HEALTH_ENABLED", "true");
|
||||
envVars.put("CAMELEER_AGENT_HEALTH_PORT", String.valueOf(agentHealthPort));
|
||||
if (bootstrapToken != null && !bootstrapToken.isBlank()) {
|
||||
envVars.put("CAMELEER_AGENT_AUTH_TOKEN", bootstrapToken);
|
||||
}
|
||||
@@ -343,6 +368,8 @@ public class DeploymentExecutor {
|
||||
map.put("serverUrl", config.serverUrl());
|
||||
map.put("replicas", config.replicas());
|
||||
map.put("deploymentStrategy", config.deploymentStrategy());
|
||||
map.put("runtimeType", config.runtimeType());
|
||||
map.put("customArgs", config.customArgs());
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,10 +84,12 @@ public class DockerRuntimeOrchestrator implements RuntimeOrchestrator {
|
||||
hostConfig.withCpuQuota(request.cpuQuota());
|
||||
}
|
||||
|
||||
// When using volume mount, the JAR is at the original path, not /app/app.jar
|
||||
// Resolve the JAR path for the entrypoint
|
||||
String appJarPath;
|
||||
if (request.jarVolumeName() != null && !request.jarVolumeName().isBlank()) {
|
||||
envList = new ArrayList<>(envList);
|
||||
envList.add("CAMELEER_APP_JAR=" + request.jarPath());
|
||||
appJarPath = request.jarPath();
|
||||
} else {
|
||||
appJarPath = "/app/app.jar";
|
||||
}
|
||||
|
||||
var createCmd = dockerClient.createContainerCmd(request.baseImage())
|
||||
@@ -103,21 +105,19 @@ public class DockerRuntimeOrchestrator implements RuntimeOrchestrator {
|
||||
.withRetries(3)
|
||||
.withStartPeriod(30_000_000_000L));
|
||||
|
||||
// Override entrypoint to use the volume-mounted JAR path
|
||||
if (request.jarVolumeName() != null && !request.jarVolumeName().isBlank()) {
|
||||
createCmd.withEntrypoint("sh", "-c",
|
||||
"exec java -javaagent:/app/agent.jar " +
|
||||
"-Dcameleer.export.type=${CAMELEER_EXPORT_TYPE:-HTTP} " +
|
||||
"-Dcameleer.export.endpoint=${CAMELEER_SERVER_URL} " +
|
||||
"-Dcameleer.agent.name=${HOSTNAME} " +
|
||||
"-Dcameleer.agent.application=${CAMELEER_APPLICATION_ID:-default} " +
|
||||
"-Dcameleer.agent.environment=${CAMELEER_ENVIRONMENT_ID:-default} " +
|
||||
"-Dcameleer.routeControl.enabled=${CAMELEER_ROUTE_CONTROL_ENABLED:-false} " +
|
||||
"-Dcameleer.replay.enabled=${CAMELEER_REPLAY_ENABLED:-false} " +
|
||||
"-Dcameleer.health.enabled=true " +
|
||||
"-Dcameleer.health.port=9464 " +
|
||||
"-jar ${CAMELEER_APP_JAR}");
|
||||
}
|
||||
// Build entrypoint based on runtime type
|
||||
String customArgs = request.customArgs() != null && !request.customArgs().isBlank()
|
||||
? " " + request.customArgs() : "";
|
||||
String entrypoint = switch (request.runtimeType()) {
|
||||
case "quarkus" -> "exec java -javaagent:/app/agent.jar" + customArgs + " -jar " + appJarPath;
|
||||
case "plain-java" -> "exec java -javaagent:/app/agent.jar -cp " + appJarPath +
|
||||
":/app/cameleer3-log-appender.jar" + customArgs + " " + request.mainClass();
|
||||
case "native" -> "exec " + appJarPath + customArgs;
|
||||
default -> // spring-boot (default)
|
||||
"exec java -javaagent:/app/agent.jar -Dloader.path=/app/cameleer3-log-appender.jar" +
|
||||
customArgs + " -cp " + appJarPath + " org.springframework.boot.loader.launch.PropertiesLauncher";
|
||||
};
|
||||
createCmd.withEntrypoint("sh", "-c", entrypoint);
|
||||
|
||||
if (request.exposedPorts() != null && !request.exposedPorts().isEmpty()) {
|
||||
var ports = request.exposedPorts().stream()
|
||||
|
||||
@@ -32,6 +32,11 @@ public class AppService {
|
||||
public App getBySlug(String slug) { return appRepo.findBySlug(slug).orElseThrow(() -> new IllegalArgumentException("App not found: " + slug)); }
|
||||
public List<AppVersion> listVersions(UUID appId) { return versionRepo.findByAppId(appId); }
|
||||
|
||||
public AppVersion getVersion(UUID versionId) {
|
||||
return versionRepo.findById(versionId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("AppVersion not found: " + versionId));
|
||||
}
|
||||
|
||||
public void updateContainerConfig(UUID id, Map<String, Object> containerConfig) {
|
||||
getById(id); // verify exists
|
||||
appRepo.updateContainerConfig(id, containerConfig);
|
||||
|
||||
@@ -20,5 +20,8 @@ public record ContainerRequest(
|
||||
List<Integer> exposedPorts,
|
||||
int healthCheckPort,
|
||||
String restartPolicyName,
|
||||
int restartPolicyMaxRetries
|
||||
int restartPolicyMaxRetries,
|
||||
String runtimeType,
|
||||
String customArgs,
|
||||
String mainClass
|
||||
) {}
|
||||
|
||||
Reference in New Issue
Block a user