From 6e0a088183dc8642617ea23cc3a6d74924c783d7 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:16:18 +0200 Subject: [PATCH] feat: Spring Boot backend with deploy pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - REST API: POST/GET/DELETE /api/apps - Build pipeline: JAR → Dockerfile → docker build → push → kubectl apply - In-memory state with K8s reconciliation on startup - Shell executor for docker/kubectl commands - Configurable via env vars (server URL, bootstrap token, registry, namespace) Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitattributes | 1 + pom.xml | 43 +++ .../deploy/DeployDemoApplication.java | 11 + .../deploy/config/DeployProperties.java | 33 ++ .../com/cameleer/deploy/config/WebConfig.java | 16 + .../deploy/controller/DeployController.java | 94 ++++++ .../cameleer/deploy/model/DeployRequest.java | 12 + .../cameleer/deploy/model/DeployStatus.java | 11 + .../cameleer/deploy/model/DeployedApp.java | 23 ++ .../cameleer/deploy/model/ResourceConfig.java | 12 + .../deploy/service/DeployService.java | 312 ++++++++++++++++++ .../deploy/service/ShellExecutor.java | 32 ++ src/main/resources/application.yml | 17 + target/classes/application.yml | 17 + .../deploy/DeployDemoApplication.class | Bin 0 -> 757 bytes .../deploy/config/DeployProperties.class | Bin 0 -> 2087 bytes .../cameleer/deploy/config/WebConfig.class | Bin 0 -> 1348 bytes .../controller/DeployController$1.class | Bin 0 -> 997 bytes .../deploy/controller/DeployController.class | Bin 0 -> 6886 bytes .../cameleer/deploy/model/DeployRequest.class | Bin 0 -> 2600 bytes .../cameleer/deploy/model/DeployStatus.class | Bin 0 -> 1460 bytes .../cameleer/deploy/model/DeployedApp.class | Bin 0 -> 3711 bytes .../deploy/model/ResourceConfig.class | Bin 0 -> 2097 bytes .../deploy/service/DeployService.class | Bin 0 -> 20023 bytes .../deploy/service/ShellExecutor.class | Bin 0 -> 2718 bytes .../compile/default-compile/createdFiles.lst | 11 + .../compile/default-compile/inputFiles.lst | 10 + 27 files changed, 655 insertions(+) create mode 100644 .gitattributes create mode 100644 pom.xml create mode 100644 src/main/java/com/cameleer/deploy/DeployDemoApplication.java create mode 100644 src/main/java/com/cameleer/deploy/config/DeployProperties.java create mode 100644 src/main/java/com/cameleer/deploy/config/WebConfig.java create mode 100644 src/main/java/com/cameleer/deploy/controller/DeployController.java create mode 100644 src/main/java/com/cameleer/deploy/model/DeployRequest.java create mode 100644 src/main/java/com/cameleer/deploy/model/DeployStatus.java create mode 100644 src/main/java/com/cameleer/deploy/model/DeployedApp.java create mode 100644 src/main/java/com/cameleer/deploy/model/ResourceConfig.java create mode 100644 src/main/java/com/cameleer/deploy/service/DeployService.java create mode 100644 src/main/java/com/cameleer/deploy/service/ShellExecutor.java create mode 100644 src/main/resources/application.yml create mode 100644 target/classes/application.yml create mode 100644 target/classes/com/cameleer/deploy/DeployDemoApplication.class create mode 100644 target/classes/com/cameleer/deploy/config/DeployProperties.class create mode 100644 target/classes/com/cameleer/deploy/config/WebConfig.class create mode 100644 target/classes/com/cameleer/deploy/controller/DeployController$1.class create mode 100644 target/classes/com/cameleer/deploy/controller/DeployController.class create mode 100644 target/classes/com/cameleer/deploy/model/DeployRequest.class create mode 100644 target/classes/com/cameleer/deploy/model/DeployStatus.class create mode 100644 target/classes/com/cameleer/deploy/model/DeployedApp.class create mode 100644 target/classes/com/cameleer/deploy/model/ResourceConfig.class create mode 100644 target/classes/com/cameleer/deploy/service/DeployService.class create mode 100644 target/classes/com/cameleer/deploy/service/ShellExecutor.class create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..008434d --- /dev/null +++ b/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.4.3 + + + + com.cameleer + cameleer-deploy-demo + 0.1.0-SNAPSHOT + Cameleer Deploy Demo + Demo: upload Camel JARs, build containers with agent injection, deploy to K8s + + + 21 + + + + + org.springframework.boot + spring-boot-starter-web + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/src/main/java/com/cameleer/deploy/DeployDemoApplication.java b/src/main/java/com/cameleer/deploy/DeployDemoApplication.java new file mode 100644 index 0000000..d6e6871 --- /dev/null +++ b/src/main/java/com/cameleer/deploy/DeployDemoApplication.java @@ -0,0 +1,11 @@ +package com.cameleer.deploy; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DeployDemoApplication { + public static void main(String[] args) { + SpringApplication.run(DeployDemoApplication.class, args); + } +} diff --git a/src/main/java/com/cameleer/deploy/config/DeployProperties.java b/src/main/java/com/cameleer/deploy/config/DeployProperties.java new file mode 100644 index 0000000..34e9e94 --- /dev/null +++ b/src/main/java/com/cameleer/deploy/config/DeployProperties.java @@ -0,0 +1,33 @@ +package com.cameleer.deploy.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "cameleer.deploy") +public class DeployProperties { + private String serverUrl = "http://cameleer3-server.cameleer.svc:8081"; + private String bootstrapToken = "changeme"; + private String registry = "gitea.siegeln.net/cameleer/demo-apps"; + private String agentMavenUrl; + private String demoNamespace = "cameleer-demo"; + private String cameleerServerUi = "http://localhost:8081"; + + public String getServerUrl() { return serverUrl; } + public void setServerUrl(String serverUrl) { this.serverUrl = serverUrl; } + + public String getBootstrapToken() { return bootstrapToken; } + public void setBootstrapToken(String bootstrapToken) { this.bootstrapToken = bootstrapToken; } + + public String getRegistry() { return registry; } + public void setRegistry(String registry) { this.registry = registry; } + + public String getAgentMavenUrl() { return agentMavenUrl; } + public void setAgentMavenUrl(String agentMavenUrl) { this.agentMavenUrl = agentMavenUrl; } + + public String getDemoNamespace() { return demoNamespace; } + public void setDemoNamespace(String demoNamespace) { this.demoNamespace = demoNamespace; } + + public String getCameleerServerUi() { return cameleerServerUi; } + public void setCameleerServerUi(String cameleerServerUi) { this.cameleerServerUi = cameleerServerUi; } +} diff --git a/src/main/java/com/cameleer/deploy/config/WebConfig.java b/src/main/java/com/cameleer/deploy/config/WebConfig.java new file mode 100644 index 0000000..f89cbc8 --- /dev/null +++ b/src/main/java/com/cameleer/deploy/config/WebConfig.java @@ -0,0 +1,16 @@ +package com.cameleer.deploy.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/api/**") + .allowedOriginPatterns("*") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*"); + } +} diff --git a/src/main/java/com/cameleer/deploy/controller/DeployController.java b/src/main/java/com/cameleer/deploy/controller/DeployController.java new file mode 100644 index 0000000..eae6413 --- /dev/null +++ b/src/main/java/com/cameleer/deploy/controller/DeployController.java @@ -0,0 +1,94 @@ +package com.cameleer.deploy.controller; + +import com.cameleer.deploy.config.DeployProperties; +import com.cameleer.deploy.model.DeployRequest; +import com.cameleer.deploy.model.DeployedApp; +import com.cameleer.deploy.service.DeployService; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/apps") +public class DeployController { + + private final DeployService deployService; + private final DeployProperties props; + private final ObjectMapper objectMapper; + + public DeployController(DeployService deployService, DeployProperties props, ObjectMapper objectMapper) { + this.deployService = deployService; + this.props = props; + this.objectMapper = objectMapper; + } + + @GetMapping + public List list() { + return deployService.listApps(); + } + + @GetMapping("/{name}") + public ResponseEntity get(@PathVariable String name) { + return deployService.getApp(name) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + @GetMapping("/{name}/logs") + public List logs(@PathVariable String name) { + return deployService.getBuildLog(name); + } + + @PostMapping + public ResponseEntity deploy( + @RequestParam("name") String name, + @RequestParam("jar") MultipartFile jar, + @RequestParam(value = "cpuRequest", required = false) String cpuRequest, + @RequestParam(value = "memoryRequest", required = false) String memoryRequest, + @RequestParam(value = "cpuLimit", required = false) String cpuLimit, + @RequestParam(value = "memoryLimit", required = false) String memoryLimit, + @RequestParam(value = "envVars", required = false) String envVarsJson + ) { + try { + if (!name.matches("^[a-z0-9]([a-z0-9-]*[a-z0-9])?$")) { + return ResponseEntity.badRequest().build(); + } + if (jar.isEmpty() || !jar.getOriginalFilename().endsWith(".jar")) { + return ResponseEntity.badRequest().build(); + } + + Map envVars = Map.of(); + if (envVarsJson != null && !envVarsJson.isBlank()) { + envVars = objectMapper.readValue(envVarsJson, new TypeReference<>() {}); + } + + var request = new DeployRequest(name, cpuRequest, memoryRequest, cpuLimit, memoryLimit, envVars); + var app = deployService.deploy(name, jar, request); + return ResponseEntity.accepted().body(app); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().build(); + } catch (Exception e) { + return ResponseEntity.internalServerError().build(); + } + } + + @DeleteMapping("/{name}") + public ResponseEntity undeploy(@PathVariable String name) { + try { + deployService.undeploy(name); + return ResponseEntity.noContent().build(); + } catch (IllegalArgumentException e) { + return ResponseEntity.notFound().build(); + } + } + + @GetMapping("/config") + public Map config() { + return Map.of("cameleerServerUi", props.getCameleerServerUi()); + } +} diff --git a/src/main/java/com/cameleer/deploy/model/DeployRequest.java b/src/main/java/com/cameleer/deploy/model/DeployRequest.java new file mode 100644 index 0000000..e2a4a54 --- /dev/null +++ b/src/main/java/com/cameleer/deploy/model/DeployRequest.java @@ -0,0 +1,12 @@ +package com.cameleer.deploy.model; + +import java.util.Map; + +public record DeployRequest( + String name, + String cpuRequest, + String memoryRequest, + String cpuLimit, + String memoryLimit, + Map envVars +) {} diff --git a/src/main/java/com/cameleer/deploy/model/DeployStatus.java b/src/main/java/com/cameleer/deploy/model/DeployStatus.java new file mode 100644 index 0000000..4afaade --- /dev/null +++ b/src/main/java/com/cameleer/deploy/model/DeployStatus.java @@ -0,0 +1,11 @@ +package com.cameleer.deploy.model; + +public enum DeployStatus { + BUILDING, + PUSHING, + DEPLOYING, + RUNNING, + PENDING, + FAILED, + DELETED +} diff --git a/src/main/java/com/cameleer/deploy/model/DeployedApp.java b/src/main/java/com/cameleer/deploy/model/DeployedApp.java new file mode 100644 index 0000000..1e87580 --- /dev/null +++ b/src/main/java/com/cameleer/deploy/model/DeployedApp.java @@ -0,0 +1,23 @@ +package com.cameleer.deploy.model; + +import java.time.Instant; +import java.util.Map; + +public record DeployedApp( + String name, + String imageName, + String imageTag, + DeployStatus status, + String statusMessage, + ResourceConfig resources, + Map envVars, + Instant createdAt +) { + public DeployedApp withStatus(DeployStatus status, String message) { + return new DeployedApp(name, imageName, imageTag, status, message, resources, envVars, createdAt); + } + + public DeployedApp withImage(String imageName, String imageTag) { + return new DeployedApp(name, imageName, imageTag, status, statusMessage, resources, envVars, createdAt); + } +} diff --git a/src/main/java/com/cameleer/deploy/model/ResourceConfig.java b/src/main/java/com/cameleer/deploy/model/ResourceConfig.java new file mode 100644 index 0000000..14e1e36 --- /dev/null +++ b/src/main/java/com/cameleer/deploy/model/ResourceConfig.java @@ -0,0 +1,12 @@ +package com.cameleer.deploy.model; + +public record ResourceConfig( + String cpuRequest, + String memoryRequest, + String cpuLimit, + String memoryLimit +) { + public static ResourceConfig defaults() { + return new ResourceConfig("250m", "256Mi", "500m", "512Mi"); + } +} diff --git a/src/main/java/com/cameleer/deploy/service/DeployService.java b/src/main/java/com/cameleer/deploy/service/DeployService.java new file mode 100644 index 0000000..e5a22bf --- /dev/null +++ b/src/main/java/com/cameleer/deploy/service/DeployService.java @@ -0,0 +1,312 @@ +package com.cameleer.deploy.service; + +import com.cameleer.deploy.config.DeployProperties; +import com.cameleer.deploy.model.*; +import jakarta.annotation.PostConstruct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@Service +public class DeployService { + + private static final Logger log = LoggerFactory.getLogger(DeployService.class); + + private final ConcurrentHashMap apps = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> buildLogs = new ConcurrentHashMap<>(); + private final DeployProperties props; + private final ShellExecutor shell; + + public DeployService(DeployProperties props, ShellExecutor shell) { + this.props = props; + this.shell = shell; + } + + @PostConstruct + public void rediscoverApps() { + try { + String output = shell.exec("kubectl", "get", "deployments", + "-n", props.getDemoNamespace(), + "-l", "app.kubernetes.io/managed-by=cameleer-deploy", + "-o", "jsonpath={range .items[*]}{.metadata.name}|{.spec.template.spec.containers[0].image}|{.metadata.creationTimestamp}{\"\\n\"}{end}"); + + for (String line : output.split("\n")) { + line = line.trim(); + if (line.isEmpty()) continue; + String[] parts = line.split("\\|"); + if (parts.length < 3) continue; + + String name = parts[0]; + String fullImage = parts[1]; + String created = parts[2]; + + String imageName = fullImage.contains(":") ? fullImage.substring(0, fullImage.lastIndexOf(':')) : fullImage; + String imageTag = fullImage.contains(":") ? fullImage.substring(fullImage.lastIndexOf(':') + 1) : "latest"; + + apps.put(name, new DeployedApp( + name, imageName, imageTag, + DeployStatus.RUNNING, null, + ResourceConfig.defaults(), + Map.of(), + Instant.parse(created) + )); + log.info("Rediscovered app: {}", name); + } + } catch (Exception e) { + log.warn("Could not rediscover apps from namespace {}: {}", props.getDemoNamespace(), e.getMessage()); + } + } + + public List listApps() { + reconcileStatuses(); + return apps.values().stream() + .filter(a -> a.status() != DeployStatus.DELETED) + .sorted(Comparator.comparing(DeployedApp::createdAt).reversed()) + .toList(); + } + + public Optional getApp(String name) { + return Optional.ofNullable(apps.get(name)); + } + + public List getBuildLog(String name) { + return buildLogs.getOrDefault(name, List.of()); + } + + public DeployedApp deploy(String name, MultipartFile jarFile, DeployRequest request) throws IOException { + if (apps.containsKey(name) && apps.get(name).status() != DeployStatus.DELETED) { + throw new IllegalArgumentException("App '" + name + "' already exists"); + } + + var resources = new ResourceConfig( + request.cpuRequest() != null ? request.cpuRequest() : "250m", + request.memoryRequest() != null ? request.memoryRequest() : "256Mi", + request.cpuLimit() != null ? request.cpuLimit() : "500m", + request.memoryLimit() != null ? request.memoryLimit() : "512Mi" + ); + + var envVars = request.envVars() != null ? request.envVars() : Map.of(); + String imageTag = String.valueOf(System.currentTimeMillis()); + String imageName = props.getRegistry() + "/" + name; + + var app = new DeployedApp(name, imageName, imageTag, + DeployStatus.BUILDING, null, resources, envVars, Instant.now()); + apps.put(name, app); + buildLogs.put(name, Collections.synchronizedList(new ArrayList<>())); + + // Run pipeline async + Thread.ofVirtual().name("deploy-" + name).start(() -> { + try { + runPipeline(name, jarFile.getBytes(), imageName, imageTag, resources, envVars); + } catch (Exception e) { + log.error("Deploy pipeline failed for {}", name, e); + appendLog(name, "FATAL: " + e.getMessage()); + apps.computeIfPresent(name, (k, v) -> v.withStatus(DeployStatus.FAILED, e.getMessage())); + } + }); + + return app; + } + + public void undeploy(String name) { + var app = apps.get(name); + if (app == null) throw new IllegalArgumentException("App '" + name + "' not found"); + + try { + appendLog(name, "Deleting deployment..."); + shell.exec("kubectl", "delete", "deployment", name, + "-n", props.getDemoNamespace(), "--ignore-not-found=true"); + appendLog(name, "Deployment deleted."); + + apps.computeIfPresent(name, (k, v) -> v.withStatus(DeployStatus.DELETED, "Undeployed")); + } catch (Exception e) { + log.error("Failed to undeploy {}", name, e); + throw new RuntimeException("Failed to undeploy: " + e.getMessage()); + } + } + + private void runPipeline(String name, byte[] jarBytes, String imageName, + String imageTag, ResourceConfig resources, + Map envVars) throws Exception { + Path buildDir = Files.createTempDirectory("cameleer-build-" + name); + try { + // 1. Write JAR + appendLog(name, "Saving JAR file..."); + Path jarPath = buildDir.resolve("app.jar"); + Files.write(jarPath, jarBytes); + appendLog(name, "JAR saved (" + jarBytes.length / 1024 + " KB)"); + + // 2. Generate Dockerfile + appendLog(name, "Generating Dockerfile..."); + String dockerfile = generateDockerfile(name); + Files.writeString(buildDir.resolve("Dockerfile"), dockerfile); + appendLog(name, "Dockerfile generated"); + + // 3. Build image + String fullTag = imageName + ":" + imageTag; + appendLog(name, "Building image: " + fullTag); + apps.computeIfPresent(name, (k, v) -> v.withStatus(DeployStatus.BUILDING, "docker build")); + + String buildOutput = shell.exec("docker", "build", "-t", fullTag, buildDir.toString()); + for (String line : buildOutput.split("\n")) { + appendLog(name, "[build] " + line); + } + appendLog(name, "Image built successfully"); + + // 4. Push image + appendLog(name, "Pushing image to registry..."); + apps.computeIfPresent(name, (k, v) -> v.withStatus(DeployStatus.PUSHING, "docker push")); + + String pushOutput = shell.exec("docker", "push", fullTag); + for (String line : pushOutput.split("\n")) { + if (!line.isBlank()) appendLog(name, "[push] " + line); + } + appendLog(name, "Image pushed successfully"); + + // 5. Deploy to K8s + appendLog(name, "Deploying to Kubernetes..."); + apps.computeIfPresent(name, (k, v) -> v.withStatus(DeployStatus.DEPLOYING, "kubectl apply")); + + String manifest = generateManifest(name, fullTag, resources, envVars); + Path manifestPath = buildDir.resolve("deployment.yaml"); + Files.writeString(manifestPath, manifest); + + String applyOutput = shell.exec("kubectl", "apply", "-f", manifestPath.toString()); + appendLog(name, "[kubectl] " + applyOutput.trim()); + + apps.computeIfPresent(name, (k, v) -> v + .withImage(imageName, imageTag) + .withStatus(DeployStatus.PENDING, "Waiting for pod to start")); + appendLog(name, "Deployment applied. Waiting for pod..."); + + } finally { + // Cleanup + try { + Files.walk(buildDir) + .sorted(Comparator.reverseOrder()) + .forEach(p -> { try { Files.delete(p); } catch (IOException ignored) {} }); + } catch (IOException ignored) {} + } + } + + private String generateDockerfile(String appName) { + return """ + FROM eclipse-temurin:21-jre-alpine + + ADD %s /opt/cameleer/agent.jar + + COPY app.jar /opt/app/app.jar + + ENV CAMELEER_SERVER_URL=%s + ENV CAMELEER_APP_NAME=%s + ENV CAMELEER_AUTH_TOKEN=%s + + ENTRYPOINT ["java", "-javaagent:/opt/cameleer/agent.jar", "-jar", "/opt/app/app.jar"] + """.formatted( + props.getAgentMavenUrl(), + props.getServerUrl(), + appName, + props.getBootstrapToken() + ); + } + + private String generateManifest(String name, String image, + ResourceConfig resources, + Map envVars) { + var envLines = new StringBuilder(); + // Standard cameleer env vars are baked into the image, add user-provided ones + for (var entry : envVars.entrySet()) { + envLines.append(""" + - name: %s + value: "%s" + """.formatted(entry.getKey(), entry.getValue().replace("\"", "\\\""))); + } + + return """ + apiVersion: apps/v1 + kind: Deployment + metadata: + name: %s + namespace: %s + labels: + app.kubernetes.io/managed-by: cameleer-deploy + cameleer/app-name: %s + spec: + replicas: 1 + selector: + matchLabels: + app: %s + template: + metadata: + labels: + app: %s + cameleer/app-name: %s + spec: + containers: + - name: app + image: %s + resources: + requests: + cpu: %s + memory: %s + limits: + cpu: %s + memory: %s + env: + %s ports: + - containerPort: 8080 + """.formatted( + name, props.getDemoNamespace(), name, + name, name, name, + image, + resources.cpuRequest(), resources.memoryRequest(), + resources.cpuLimit(), resources.memoryLimit(), + envLines.toString() + ); + } + + private void reconcileStatuses() { + for (var entry : apps.entrySet()) { + var app = entry.getValue(); + if (app.status() == DeployStatus.DELETED || app.status() == DeployStatus.BUILDING + || app.status() == DeployStatus.PUSHING) { + continue; + } + try { + String phase = shell.exec("kubectl", "get", "pods", + "-n", props.getDemoNamespace(), + "-l", "app=" + app.name(), + "-o", "jsonpath={.items[0].status.phase}").trim(); + + DeployStatus newStatus = switch (phase) { + case "Running" -> DeployStatus.RUNNING; + case "Pending" -> DeployStatus.PENDING; + case "Failed", "Unknown" -> DeployStatus.FAILED; + default -> app.status(); + }; + + if (newStatus != app.status()) { + apps.computeIfPresent(entry.getKey(), + (k, v) -> v.withStatus(newStatus, phase)); + } + } catch (Exception e) { + // Pod might not exist yet + } + } + } + + private void appendLog(String name, String line) { + var logList = buildLogs.get(name); + if (logList != null) { + logList.add("[" + Instant.now() + "] " + line); + } + } +} diff --git a/src/main/java/com/cameleer/deploy/service/ShellExecutor.java b/src/main/java/com/cameleer/deploy/service/ShellExecutor.java new file mode 100644 index 0000000..14d2e9e --- /dev/null +++ b/src/main/java/com/cameleer/deploy/service/ShellExecutor.java @@ -0,0 +1,32 @@ +package com.cameleer.deploy.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +@Component +public class ShellExecutor { + + private static final Logger log = LoggerFactory.getLogger(ShellExecutor.class); + + public String exec(String... command) throws IOException, InterruptedException { + log.debug("Executing: {}", String.join(" ", command)); + var pb = new ProcessBuilder(command) + .redirectErrorStream(true); + var process = pb.start(); + String output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8); + boolean finished = process.waitFor(5, TimeUnit.MINUTES); + if (!finished) { + process.destroyForcibly(); + throw new RuntimeException("Command timed out: " + String.join(" ", command)); + } + if (process.exitValue() != 0) { + throw new RuntimeException("Command failed (exit " + process.exitValue() + "): " + output); + } + return output; + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..a6b5e5f --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,17 @@ +server: + port: 8082 + +spring: + servlet: + multipart: + max-file-size: 200MB + max-request-size: 200MB + +cameleer: + deploy: + server-url: ${CAMELEER_SERVER_URL:http://cameleer3-server.cameleer.svc:8081} + bootstrap-token: ${CAMELEER_BOOTSTRAP_TOKEN:changeme} + registry: ${CAMELEER_REGISTRY:gitea.siegeln.net/cameleer/demo-apps} + agent-maven-url: ${CAMELEER_AGENT_MAVEN_URL:https://gitea.siegeln.net/api/packages/cameleer/maven/com/cameleer3/cameleer3-agent/1.0-SNAPSHOT/cameleer3-agent-1.0-SNAPSHOT.jar} + demo-namespace: ${CAMELEER_DEMO_NAMESPACE:cameleer-demo} + cameleer-server-ui: ${CAMELEER_SERVER_UI:http://localhost:8081} diff --git a/target/classes/application.yml b/target/classes/application.yml new file mode 100644 index 0000000..a6b5e5f --- /dev/null +++ b/target/classes/application.yml @@ -0,0 +1,17 @@ +server: + port: 8082 + +spring: + servlet: + multipart: + max-file-size: 200MB + max-request-size: 200MB + +cameleer: + deploy: + server-url: ${CAMELEER_SERVER_URL:http://cameleer3-server.cameleer.svc:8081} + bootstrap-token: ${CAMELEER_BOOTSTRAP_TOKEN:changeme} + registry: ${CAMELEER_REGISTRY:gitea.siegeln.net/cameleer/demo-apps} + agent-maven-url: ${CAMELEER_AGENT_MAVEN_URL:https://gitea.siegeln.net/api/packages/cameleer/maven/com/cameleer3/cameleer3-agent/1.0-SNAPSHOT/cameleer3-agent-1.0-SNAPSHOT.jar} + demo-namespace: ${CAMELEER_DEMO_NAMESPACE:cameleer-demo} + cameleer-server-ui: ${CAMELEER_SERVER_UI:http://localhost:8081} diff --git a/target/classes/com/cameleer/deploy/DeployDemoApplication.class b/target/classes/com/cameleer/deploy/DeployDemoApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..eeaf070b7b4ee9a11b7c964738d569f62b5e0a5f GIT binary patch literal 757 zcma)4O;6iE5PcgG9MTkM_$p8i+>%u4-g-%e1VKF+sG2H0a9TTyS+IAb^%~lr)f0#V zKY$;Fn01kogAZ8R8O_(5@tb*ies~0Mh`knSSO`#Wqk%<+)hlr$cq(+l&!Q_CJBGz? zN-KB3u+Z&|12h?WvB`KWGMP$g`9w}r^Oql&>R4vxa5_y@ESxgBg%;igXt(hm%M5*E z6P{14(#eIT`deeKd1Q>^qtZE7N$qT*8KQ1D8)=w|JRh9Tc}C8I4SHb}a%{Ade;gkg zeW8-ViYS$HEy(S|1BUw0OeDifsI)vSvPjxrK9`{rnpmV`VU@REoppDqa)!_0n`!VZ zGNI^qHoH~Rk0~x}LZE9EVcN=xbeCrGLwM4hw3OHynZm~MyYgf1RFeP26UVUqv(Qdu za;$RY3DDZOayf)@P{~`l5m7ip1eV0f*ZmyPs^$nBU>^b9kN;|5{(?xZki|(e(vAKd z*uOHoO45fxB_Bw(|0ke>HL{kljtv6w#a~M>pxFuf_gMNptM|w1a|1f78AcPE*dqT& UdJUqOR1Mpu+`%qs-}V#m0;prgod5s; literal 0 HcmV?d00001 diff --git a/target/classes/com/cameleer/deploy/config/DeployProperties.class b/target/classes/com/cameleer/deploy/config/DeployProperties.class new file mode 100644 index 0000000000000000000000000000000000000000..5558bf675e4490e6a6b9b02993944085ed719442 GIT binary patch literal 2087 zcmb7^S##4!5XXB=4kb~DkHnB`34|jS*y7yHCM-(`@MMl6katDt$OE=UHJSwSt#+$$ zQMC{I0ro>}ZMSU8i6TGji(1qAch7Hny7kY$fBg*rJFqhi1-LW`V>XP#gn;5_@5FQ4 zUKqIV4nCuA1WY`Yp)}6~T-vDZTVM&OwTy|jT-WzH*hbWk>M`mQ)Qy3o5uf;5oA);# zOu;nFOoDC0EL;|F*H@i1%00xWtxjEEg-0@QUnZ;fT1BXh#8|*oFY71W7Eo>u!S;-n zVX$pMQGn&Qh!s1?JC$vy@HUqZlrpi=Ui49Y!O(&Q0XG9_&}+mJ18j$l5Y1VTj;ec6 z6mya*HeBT-mc~F*l2Z$o1k4VORzJj!V%>@jtE`)4$6KmI9C6-zIH}@us)db_D+b0R2f%=RJ;BD z9bAHd3JEWIr}xyc_VKmkqAz3{wZWY!VDZCoXk-WXWi077cEV5@&qx*0;hm<^fg4BM zw?|x)uS$P$W2y`)a~h$$t2&Vi>DK8cUge6prFm2gelu>AP6n`q7-3t0Tar_;H$aG6L@B@|3bXm0X?&`8=g8&-@4;zNM6>lJGpt<*8J+05lx%ko^y<@q(A=gfHZ`gv^x4^MH*bAxyaG#LEchNoob P=N=27;m+7eVmNr+YrZ4h^d^vC8Dpj*sD!(bNxjOV&3)gkr$v)*( z5p4yHP@8U8NF6o37Q7);XduHdTMt@Jol5Qrp`3;1!6v?VFqbA`VlnI%~2Lfx1B zPNb)~)L?zLmrYj~Om)!c%Y))k1NTZ(;(F4kp@?R}4LYhWUP_&cFog7S@sE6HTqE%@IzF#@f;*}=Nti*Jz7K|Z`b)H=FND@xq&XxR$#~`q z*ypZ!fmUq>Ofmyj?*eY)4rvT`agRXs<4*)ML~?oFHuiBM_6=jzVf&Q3kJDyvKjl_q z_64^Zw=cQX5qsLL8ukp?uN}ENw{hrqk`RGWjFDG|eyc2!ct)D$G1>Fg=Rsa^9}kGn WBH7}M6dl{c?)nIiN$Mb;0KWh^oq3S} literal 0 HcmV?d00001 diff --git a/target/classes/com/cameleer/deploy/controller/DeployController$1.class b/target/classes/com/cameleer/deploy/controller/DeployController$1.class new file mode 100644 index 0000000000000000000000000000000000000000..0cccb82e9c8d49cea1e78a17cdb65e73d49b6241 GIT binary patch literal 997 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 Gqxu(ADl_Z= literal 0 HcmV?d00001 diff --git a/target/classes/com/cameleer/deploy/model/DeployStatus.class b/target/classes/com/cameleer/deploy/model/DeployStatus.class new file mode 100644 index 0000000000000000000000000000000000000000..646e7c8fba5b8522c7b433b027914574d008ee11 GIT binary patch literal 1460 zcma)5?@!ZE6g_Xh+SLxq5ClY2K-fT;D2TFw3^rg%9mK+tMZc7-LP^(|vXS`B|H6Mm zMT1t{eqq=6t@@>dUx_P*UKuRTrchJ8`~u3+%**{ z=c2j%uD(q~koQrH`3wl1ssT(SWL|QJd=l9y& zh>DvSrNWb?tiGQ&7?Q8LUATfV+?Ft^;tuXIgey+nwWx9;sUMrCX38<$!_>~fu~liL z6-;1K!aWsJm}cm?O31s-8r45Fou;+(hG8+$q26%Jlaq7@VKhA3Jxq62;0D4!sbU7R zwB?NL+Kp9)!A>h*SxA;-gcWK>QbtsUDx+6MT*iQmAx@<{!&ScH4A@xL&WAPRaCaZs&W3T$)qa@DbzT!DJYW`HE*)5jkQ7DtJ<#V(A*jJp-Thqk#8`OEYXx^DvsaU z<&Hx905aXKe>F{Ije66oSerKQMqlS$n&Zn~o2O%#po=K44fAUSP^9Z2B3%g)=@N)Y zgD)ZtyNER5fHuOXnjFHUdx|CUdq!xSBL68Z41dP0ON@Oa#IQ{N5%DcZ`BO+EAo3R| za)lCSGZz@Y1mpREKk_rgsO>DQV3p8wdcq7V?|CW>B!a?ho??(bNi(yf=SX~_*Z(<5 zSmV18PSF6e8n8~!hLGemg%2o|l6P_AM0!RX+!U8A)Jd6ks6JTBv zKoj7REI_{ikF|E*-~|@WFw7g#K7w(`^d3Ttj%|b*FbxR{#AWGFics*H`WNuKu!$|o H4Pg5hGN>;i literal 0 HcmV?d00001 diff --git a/target/classes/com/cameleer/deploy/model/DeployedApp.class b/target/classes/com/cameleer/deploy/model/DeployedApp.class new file mode 100644 index 0000000000000000000000000000000000000000..0cc9ad2952bdec08f7cd37f2ee1ec38ed41c4877 GIT binary patch literal 3711 zcmdT`ZFAIA6n@gTY?qX7TT0!E1y+zYEvys;B^8AtQf&cgt@5Ih?q1r!Zn7p>9R8CZ zWQHAPUllEgZuKG$9mG&TY;WlHhR$g%%%7hOsoWf};s)WLg>7LlY zn5K)px+pLCw9Sv5JdHXdgEDR4uj$=>%XE^_HC<%w&A9`)=50gN+YTf%>!VCH+rlj7 z7|l4)kvHHR96Oah&dhEDrO0y@%J`Tfx7=uJkx9ppWfr+4CCpp6fKQlB;M&L zJVC{+F|$0mPY(OyEsu7e*(X&~7m?zRE_XBHd#2lynuR-ASCJ zAvo=lKg=${F~m{e7~`1WnBN=au7UiHoD|+DytL5H5Q5MBC3R5BM!ne%VN!qqVz#h z*&gf`lGff)(A(){E&09(l}HhpV1!P;!@Lg$6Pt9~{-g}828{FUt353JIX*7o0@s$(HqXb)MgIGOCns}M z0*aT;0Y!q(Ch&m-5`nOhIN=IezrcXRj#H)(+MX}`gZ^yi^Aq%wCBQa`yRzqn2euz} zrAPNT>_;8B83xbYJ%LK6*MA~^?aMe3Fxw}2o+gnS?6p+nw+zgvu+zTu!{{J4S39!? zOtqskO563_L@6&Bm{-cF%skcWcw3;f)?D#*EC`g>nzy!H9q&@vTv5vRscf#UZMzjl zxT;`Z6L?S`F6jSh8apN%`=No4aGm8QVFpiNquw61S*^x!ZWZ-o1D~MIsQXU5zsW8O zEMFw*mI`7SSi};Km1p62vB2#MgecVYX!nKeB(28J>}*!cOxq3Q!@j>Oqi4>p$Lh>> z*m1laCvw$yt}G?{F4J10w}Z6YmdSqDee6W!S|TI*>s|TW>3a#6OZCP@f?cBd5dBhm zZwbs7nNYwe!qcqHx5(?KDb4S>3h>-%*56cV*y_Pms@@61Bu*lym*I(Z+~ygc+>F*K z;y@vY+w(x_%)f044KS*3(+F7}IR)NXgK_LRK zi-_IeRrpfcnc;mW=z21~-VVc;{a$OhX$4L0`0Su3@5;CnxxFNeiY}X;6URkGe(!%7 z4pPj1>Mk5N-Pj5O8Kog*tYKT=#tG(gFlcxraOHeco>vAKtDF#HyfP)u9*#S%Of{cU z$!U~I4j{@&aH`-Mt(W=bQI;ud)*A?G@erD|bcktd`4ClWp>&A3*W4Y$P5w^M6Bvih z-_J5HtT3~BYH3!rUrY0*yF8IbJay3RD}ss0K5 zC-buTj#)F`dxI;#Qzr^#R4~iA)y%zCDOGaKI>HBlG9Iqk$V~E@XE~s5o#B21_-!Qj zMSPw&ah*~D)~&@OH2%x~a%OYfG96#wOBSG-QWpGB0H=;2+5A$F%GMvy&I44=BMSC( z(2t_?^k~lJna20ps_ literal 0 HcmV?d00001 diff --git a/target/classes/com/cameleer/deploy/service/DeployService.class b/target/classes/com/cameleer/deploy/service/DeployService.class new file mode 100644 index 0000000000000000000000000000000000000000..239352f32993b290510166231acab5fc6a78f14c GIT binary patch literal 20023 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 B+qD1y literal 0 HcmV?d00001 diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..05869aa --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,11 @@ +com\cameleer\deploy\config\WebConfig.class +com\cameleer\deploy\controller\DeployController$1.class +com\cameleer\deploy\controller\DeployController.class +com\cameleer\deploy\DeployDemoApplication.class +com\cameleer\deploy\model\DeployedApp.class +com\cameleer\deploy\service\DeployService.class +com\cameleer\deploy\config\DeployProperties.class +com\cameleer\deploy\model\DeployStatus.class +com\cameleer\deploy\service\ShellExecutor.class +com\cameleer\deploy\model\ResourceConfig.class +com\cameleer\deploy\model\DeployRequest.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..130c48e --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,10 @@ +C:\Users\Hendrik\Documents\projects\cameleer-deploy-demo\src\main\java\com\cameleer\deploy\config\DeployProperties.java +C:\Users\Hendrik\Documents\projects\cameleer-deploy-demo\src\main\java\com\cameleer\deploy\config\WebConfig.java +C:\Users\Hendrik\Documents\projects\cameleer-deploy-demo\src\main\java\com\cameleer\deploy\controller\DeployController.java +C:\Users\Hendrik\Documents\projects\cameleer-deploy-demo\src\main\java\com\cameleer\deploy\DeployDemoApplication.java +C:\Users\Hendrik\Documents\projects\cameleer-deploy-demo\src\main\java\com\cameleer\deploy\model\DeployedApp.java +C:\Users\Hendrik\Documents\projects\cameleer-deploy-demo\src\main\java\com\cameleer\deploy\model\DeployRequest.java +C:\Users\Hendrik\Documents\projects\cameleer-deploy-demo\src\main\java\com\cameleer\deploy\model\DeployStatus.java +C:\Users\Hendrik\Documents\projects\cameleer-deploy-demo\src\main\java\com\cameleer\deploy\model\ResourceConfig.java +C:\Users\Hendrik\Documents\projects\cameleer-deploy-demo\src\main\java\com\cameleer\deploy\service\DeployService.java +C:\Users\Hendrik\Documents\projects\cameleer-deploy-demo\src\main\java\com\cameleer\deploy\service\ShellExecutor.java