This is an automated email from the ASF dual-hosted git repository. marat pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
commit f7526d0dd74177bbfcad2d5953ba7cf3ce9d0f6d Author: Marat Gubaidullin <ma...@talismancloud.io> AuthorDate: Tue Sep 5 15:10:30 2023 -0400 Refactor Docker services for #817 --- karavan-web/karavan-app/pom.xml | 13 --- .../apache/camel/karavan/api/DevModeResource.java | 44 ++++------ .../camel/karavan/api/InfrastructureResource.java | 13 ++- .../apache/camel/karavan/api/ProjectResource.java | 12 +-- .../camel/karavan/docker/DockerEventListener.java | 6 +- .../camel/karavan/docker/DockerForGitea.java | 5 +- .../camel/karavan/docker/DockerForInfinispan.java | 5 +- .../camel/karavan/docker/DockerForKaravan.java | 11 ++- .../apache/camel/karavan/docker/DockerService.java | 76 ++++++++--------- .../camel/karavan/docker/DockerServiceUtils.java | 61 +------------- .../karavan/docker/model/DockerComposeService.java | 16 +++- .../apache/camel/karavan/service/CamelService.java | 35 +++++++- .../apache/camel/karavan/service/CodeService.java | 97 +++++++++++++++++++++- .../apache/camel/karavan/service/DelayRoute.java | 20 ----- .../apache/camel/karavan/service/EventService.java | 59 +++++++------ .../camel/karavan/service/KaravanService.java | 2 +- .../camel/karavan/service/ProjectService.java | 67 ++++++++++++++- .../org/apache/camel/karavan/shared/EventType.java | 3 +- .../main/resources/snippets/project-compose.yaml | 8 ++ .../src/main/webui/src/api/KaravanApi.tsx | 4 +- .../src/main/webui/src/api/ProjectService.ts | 17 ++-- .../src/main/webui/src/main/MainDataPoller.tsx | 2 - .../src/main/webui/src/project/ProjectPanel.tsx | 6 +- .../src/main/webui/src/project/files/FilesTab.tsx | 4 +- .../karavan/infinispan/InfinispanService.java | 7 ++ 25 files changed, 348 insertions(+), 245 deletions(-) diff --git a/karavan-web/karavan-app/pom.xml b/karavan-web/karavan-app/pom.xml index 96c8e5ff..387faad1 100644 --- a/karavan-web/karavan-app/pom.xml +++ b/karavan-web/karavan-app/pom.xml @@ -128,19 +128,6 @@ <artifactId>camel-openapi-rest-dsl-generator</artifactId> <version>${camel.version}</version> </dependency> - <dependency> - <groupId>org.apache.camel.quarkus</groupId> - <artifactId>camel-quarkus-core</artifactId> - </dependency> - <dependency> - <groupId>org.apache.camel.quarkus</groupId> - <artifactId>camel-quarkus-vertx</artifactId> - </dependency> - <dependency> - <groupId>org.apache.camel</groupId> - <artifactId>camel-endpointdsl</artifactId> - <version>${camel.version}</version> - </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-container-image-docker</artifactId> diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java index 5b10fa9b..3e2c879b 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java @@ -18,7 +18,10 @@ package org.apache.camel.karavan.api; import io.vertx.core.json.JsonObject; import io.vertx.mutiny.core.eventbus.EventBus; -import org.apache.camel.karavan.docker.DockerForKaravan; +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.apache.camel.karavan.docker.DockerService; import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.CamelStatus; @@ -26,16 +29,10 @@ import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.camel.karavan.infinispan.model.Project; import org.apache.camel.karavan.kubernetes.KubernetesService; import org.apache.camel.karavan.service.CamelService; +import org.apache.camel.karavan.service.ProjectService; import org.apache.camel.karavan.shared.ConfigService; -import org.apache.camel.karavan.shared.EventType; import org.eclipse.microprofile.config.inject.ConfigProperty; -import jakarta.inject.Inject; -import jakarta.ws.rs.*; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; -import java.util.Objects; - import static org.apache.camel.karavan.shared.EventType.CONTAINER_STATUS; @Path("/api/devmode") @@ -57,7 +54,7 @@ public class DevModeResource { DockerService dockerService; @Inject - DockerForKaravan dockerForKaravan; + ProjectService projectService; @Inject EventBus eventBus; @@ -66,32 +63,23 @@ public class DevModeResource { @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Path("/{jBangOptions}") - public Response runProjectWithJBangOptions(Project project, @PathParam("jBangOptions") String jBangOptions) throws InterruptedException { - String containerName = project.getProjectId(); - ContainerStatus status = infinispanService.getDevModeContainerStatus(project.getProjectId(), environment); - if (status == null) { - status = ContainerStatus.createDevMode(project.getProjectId(), environment); - } - - if (!Objects.equals(status.getState(), ContainerStatus.State.running.name())){ - status.setInTransit(true); - eventBus.send(EventType.CONTAINER_STATUS, JsonObject.mapFrom(status)); - - if (ConfigService.inKubernetes()) { - kubernetesService.runDevModeContainer(project, jBangOptions); + public Response runProjectWithJBangOptions(Project project, @PathParam("jBangOptions") String jBangOptions) { + try { + String containerName = projectService.runProjectWithJBangOptions(project, jBangOptions); + if (containerName != null) { + return Response.ok(containerName).build(); } else { - dockerForKaravan.createDevmodeContainer(project.getProjectId(), jBangOptions); - dockerService.runContainer(project.getProjectId()); + return Response.notModified().build(); } - return Response.ok(containerName).build(); + } catch (Exception e) { + return Response.serverError().entity(e).build(); } - return Response.notModified().build(); } @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - public Response runProject(Project project) throws InterruptedException { + public Response runProject(Project project) throws Exception { return runProjectWithJBangOptions(project, ""); } @@ -121,7 +109,7 @@ public class DevModeResource { return Response.accepted().build(); } - private void setContainerStatusTransit(String name, String type){ + private void setContainerStatusTransit(String name, String type) { ContainerStatus status = infinispanService.getContainerStatus(name, environment, name); if (status == null) { status = ContainerStatus.createByType(name, environment, ContainerStatus.ContainerType.valueOf(type)); diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java index 5c5f9fae..8f94786a 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java @@ -29,6 +29,7 @@ import org.apache.camel.karavan.infinispan.model.DeploymentStatus; import org.apache.camel.karavan.infinispan.model.Project; import org.apache.camel.karavan.infinispan.model.ServiceStatus; import org.apache.camel.karavan.kubernetes.KubernetesService; +import org.apache.camel.karavan.service.CodeService; import org.apache.camel.karavan.service.ProjectService; import org.apache.camel.karavan.shared.ConfigService; import org.eclipse.microprofile.config.inject.ConfigProperty; @@ -66,6 +67,9 @@ public class InfrastructureResource { @Inject ProjectService projectService; + @Inject + CodeService codeService; + @ConfigProperty(name = "karavan.environment") String environment; @@ -186,7 +190,7 @@ public class InfrastructureResource { @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Path("/container/{env}/{type}/{name}") - public Response startContainer(@PathParam("env") String env, @PathParam("type") String type, @PathParam("name") String name, JsonObject command) throws Exception { + public Response manageContainer(@PathParam("env") String env, @PathParam("type") String type, @PathParam("name") String name, JsonObject command) throws Exception { if (infinispanService.isReady()) { // set container statuses setContainerStatusTransit(name, type); @@ -195,14 +199,15 @@ public class InfrastructureResource { if (command.getString("command").equalsIgnoreCase("run")) { if (Objects.equals(type, ContainerStatus.ContainerType.devservice.name())) { String code = projectService.getDevServiceCode(); - DockerComposeService dockerComposeService = dockerService.convertToDockerComposeService(code, name); + DockerComposeService dockerComposeService = codeService.convertToDockerComposeService(code, name); if (dockerComposeService != null) { dockerForKaravan.createDevserviceContainer(dockerComposeService); dockerService.runContainer(dockerComposeService.getContainer_name()); } } else if (Objects.equals(type, ContainerStatus.ContainerType.devmode.name())) { - dockerForKaravan.createDevmodeContainer(name, ""); - dockerService.runContainer(name); +// TODO: merge with DevMode service +// dockerForKaravan.createDevmodeContainer(name, ""); +// dockerService.runContainer(name); } return Response.ok().build(); } else if (command.getString("command").equalsIgnoreCase("stop")) { diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java index cc73eae9..b62ad193 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java @@ -25,6 +25,8 @@ import org.apache.camel.karavan.service.GitService; import jakarta.inject.Inject; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; +import org.apache.camel.karavan.service.ProjectService; + import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.Comparator; @@ -43,7 +45,7 @@ public class ProjectResource { GitService gitService; @Inject - CodeService codeService; + ProjectService projectService; @GET @Produces(MediaType.APPLICATION_JSON) @@ -69,13 +71,7 @@ public class ProjectResource { @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public Project save(Project project) throws Exception { - boolean isNew = infinispanService.getProject(project.getProjectId()) == null; - infinispanService.saveProject(project); - if (isNew){ - ProjectFile appProp = codeService.getApplicationProperties(project); - infinispanService.saveProjectFile(appProp); - } - return project; + return projectService.save(project); } @DELETE diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java index e9a7b746..8ed7b546 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java @@ -50,10 +50,12 @@ public class DockerEventListener implements ResultCallback<Event> { try { if (Objects.equals(event.getType(), EventType.CONTAINER)) { Container container = dockerService.getContainer(event.getId()); - onContainerEvent(event, container); + if (container != null) { + onContainerEvent(event, container); + } } } catch (Exception exception) { - LOGGER.error(exception.getMessage()); + exception.printStackTrace(); } } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForGitea.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForGitea.java index 38397495..da52c520 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForGitea.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForGitea.java @@ -26,6 +26,7 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.camel.karavan.infinispan.model.GitConfig; +import org.apache.camel.karavan.service.CodeService; import org.apache.camel.karavan.service.GitService; import org.apache.camel.karavan.service.GiteaService; import org.jboss.logging.Logger; @@ -56,12 +57,12 @@ public class DockerForGitea { GitService gitService; @Inject - EventBus eventBus; + CodeService codeService; public void startGitea() { try { LOGGER.info("Gitea container is starting..."); - var compose = dockerService.getInternalDockerComposeService(GITEA_CONTAINER_NAME); + var compose = codeService.getInternalDockerComposeService(GITEA_CONTAINER_NAME); Container c = dockerService.createContainerFromCompose(compose, ContainerStatus.ContainerType.internal); dockerService.runContainer(GITEA_CONTAINER_NAME); LOGGER.info("Gitea container is started"); diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForInfinispan.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForInfinispan.java index b7c8c197..54774463 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForInfinispan.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForInfinispan.java @@ -20,6 +20,7 @@ import io.vertx.core.eventbus.EventBus; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.apache.camel.karavan.infinispan.model.ContainerStatus; +import org.apache.camel.karavan.service.CodeService; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; @@ -42,12 +43,12 @@ public class DockerForInfinispan { DockerService dockerService; @Inject - EventBus eventBus; + CodeService codeService; public void startInfinispan() { try { LOGGER.info("Infinispan is starting..."); - var compose = dockerService.getInternalDockerComposeService(INFINISPAN_CONTAINER_NAME); + var compose = codeService.getInternalDockerComposeService(INFINISPAN_CONTAINER_NAME); compose.addEnvironment("USER", infinispanUsername); compose.addEnvironment("PASS", infinispanPassword); dockerService.createContainerFromCompose(compose, ContainerStatus.ContainerType.internal); diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForKaravan.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForKaravan.java index c6d2b728..9ae18fa4 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForKaravan.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForKaravan.java @@ -50,7 +50,12 @@ public class DockerForKaravan { @Inject DockerService dockerService; - public void createDevmodeContainer(String projectId, String jBangOptions) throws InterruptedException { + public void runProjectInDevMode(String projectId, String jBangOptions, Map<Integer, Integer> ports, Map<String, String> files) throws Exception { + createDevmodeContainer(projectId, jBangOptions, ports); + dockerService.runContainer(projectId); + dockerService.copyFiles(projectId, "/code", files); + } + protected void createDevmodeContainer(String projectId, String jBangOptions, Map<Integer, Integer> ports) throws InterruptedException { LOGGER.infof("DevMode starting for %s with JBANG_OPTIONS=%s", projectId, jBangOptions); HealthCheck healthCheck = new HealthCheck().withTest(List.of("CMD", "curl", "-f", "http://localhost:8080/q/dev/health")) @@ -61,7 +66,7 @@ public class DockerForKaravan { : List.of(); dockerService.createContainer(projectId, devmodeImage, - env, null, false, List.of(), healthCheck, + env, ports, healthCheck, Map.of(LABEL_TYPE, ContainerStatus.ContainerType.devmode.name(), LABEL_PROJECT_ID, projectId), Map.of()); @@ -83,7 +88,7 @@ public class DockerForKaravan { "INFINISPAN_USERNAME=" + infinispanUsername, "INFINISPAN_PASSWORD=" + infinispanPassword ), - null, false, List.of(), new HealthCheck(), + null, new HealthCheck(), Map.of(LABEL_TYPE, ContainerStatus.ContainerType.internal.name()), Map.of()); diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java index e8b22d2c..5a2932c4 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java @@ -34,8 +34,10 @@ import jakarta.inject.Inject; import org.apache.camel.karavan.docker.model.DockerComposeService; import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.compress.utils.IOUtils; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; @@ -121,7 +123,7 @@ public class DockerService extends DockerServiceUtils { public Container getContainer(String id) { List<Container> containers = getDockerClient().listContainersCmd().withShowAll(true).withIdFilter(List.of(id)).exec(); - return containers.get(0); + return containers.isEmpty() ? null : containers.get(0); } public Container getContainerByName(String name) { @@ -149,14 +151,11 @@ public class DockerService extends DockerServiceUtils { HealthCheck healthCheck = getHealthCheck(compose.getHealthcheck()); List<String> env = compose.getEnvironment() != null ? compose.getEnvironmentList() : List.of(); - String ports = String.join(",", compose.getPorts()); LOGGER.infof("Compose Service started for %s", compose.getContainer_name()); return createContainer(compose.getContainer_name(), compose.getImage(), - env, ports, false, compose.getExpose(), healthCheck, - Map.of(LABEL_TYPE, type.name()), - Map.of()); + env, compose.getPortsMap(), healthCheck, Map.of(LABEL_TYPE, type.name()), Map.of()); } else { LOGGER.info("Compose Service already exists: " + containers.get(0).getId()); @@ -164,17 +163,8 @@ public class DockerService extends DockerServiceUtils { } } - public Container createContainerFromCompose(String yaml, String name, ContainerStatus.ContainerType type) throws Exception { - var compose = convertToDockerComposeService(yaml, name); - if (compose != null) { - return createContainerFromCompose(compose, type); - } else { - throw new Exception("Service not found in compose YAML!"); - } - } - - public Container createContainer(String name, String image, List<String> env, String ports, boolean inRange, - List<String> exposed, HealthCheck healthCheck, Map<String, String> labels, + public Container createContainer(String name, String image, List<String> env, Map<Integer, Integer> ports, + HealthCheck healthCheck, Map<String, String> labels, Map<String, String> volumes) throws InterruptedException { List<Container> containers = getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(name)).exec(); if (containers.size() == 0) { @@ -183,14 +173,7 @@ public class DockerService extends DockerServiceUtils { CreateContainerCmd createContainerCmd = getDockerClient().createContainerCmd(image) .withName(name).withLabels(labels).withEnv(env).withHostName(name).withHealthcheck(healthCheck); - Ports portBindings; - List<ExposedPort> exposedPorts = new ArrayList<>(); - if (exposed != null) { - exposedPorts.addAll(exposed.stream().map(i -> ExposedPort.tcp(Integer.parseInt(i))).toList()); - portBindings = getPortBindings(ports,exposedPorts, inRange); - } else { - portBindings = getPortBindings(ports,exposedPorts, inRange); - } + Ports portBindings = getPortBindings(ports); List<Mount> mounts = new ArrayList<>(); if (volumes != null && !volumes.isEmpty()) { @@ -198,8 +181,7 @@ public class DockerService extends DockerServiceUtils { mounts.add(new Mount().withType(MountType.BIND).withSource(hostPath).withTarget(containerPath)); }); } - - createContainerCmd.withExposedPorts(exposedPorts); +// createContainerCmd.withExposedPorts(exposedPorts); createContainerCmd.withHostConfig(new HostConfig() .withPortBindings(portBindings) .withMounts(mounts) @@ -260,25 +242,23 @@ public class DockerService extends DockerServiceUtils { } public void execStart(String id, ResultCallback.Adapter<Frame> callBack) throws InterruptedException { - getDockerClient().execStartCmd(id).exec(callBack).awaitCompletion(); + dockerClient.execStartCmd(id).exec(callBack).awaitCompletion(); + } + + public void copyFiles(String containerId, String containerPath, Map<String, String> files) throws IOException { + String temp = vertx.fileSystem().createTempDirectoryBlocking(containerId); + files.forEach((fileName, code) -> addFile(temp, fileName, code)); + dockerClient.copyArchiveToContainerCmd(containerId).withRemotePath(containerPath) + .withDirChildrenOnly(true).withHostResource(temp).exec(); } - public void copyFile(String id, String containerPath, String filename, String text) throws IOException { -// try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); -// TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(byteArrayOutputStream)) { -// tarArchive.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); -// tarArchive.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX); -// -// String temp = vertx.fileSystem().createTempDirectoryBlocking("x"); -// String path = temp + File.separator + filename; -// vertx.fileSystem().writeFileBlocking(path, Buffer.buffer(text)); -// -// ArchiveEntry archive = tarArchive.createArchiveEntry(Paths.get(path), "app.ini"); -// tarArchive.putArchiveEntry(archive);; -// tarArchive.finish(); - getDockerClient().copyArchiveToContainerCmd(id).withRemotePath("/data") - .withHostResource("/Users/marat/projects/camel-karavan/karavan-web/karavan-app/src/main/resources/gitea").exec(); -// } + private void addFile(String temp, String fileName, String code) { + try { + String path = temp + File.separator + fileName; + vertx.fileSystem().writeFileBlocking(path, Buffer.buffer(code)); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } } public void logContainer(String containerName, LogCallback callback) { @@ -359,4 +339,14 @@ public class DockerService extends DockerServiceUtils { } return dockerClient; } + + public int getMaxPortMapped(int port) { + return getDockerClient().listContainersCmd().withShowAll(true).exec().stream() + .map(c -> List.of(c.ports)) + .flatMap(java.util.List::stream) + .filter(p -> Objects.equals(p.getPrivatePort(), port)) + .map(ContainerPort::getPublicPort).filter(Objects::nonNull) + .mapToInt(Integer::intValue) + .max().orElse(port); + } } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerServiceUtils.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerServiceUtils.java index 703f33ae..0b8b698f 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerServiceUtils.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerServiceUtils.java @@ -42,8 +42,6 @@ import static org.apache.camel.karavan.shared.Constants.LABEL_TYPE; public class DockerServiceUtils { - protected static final String ENVIRONMENT = "environment"; - protected static final DecimalFormat formatCpu = new DecimalFormat("0.00"); protected static final DecimalFormat formatMiB = new DecimalFormat("0.0"); protected static final DecimalFormat formatGiB = new DecimalFormat("0.00"); @@ -71,46 +69,6 @@ public class DockerServiceUtils { } } - public DockerComposeService getInternalDockerComposeService (String name) { - String composeText = getResourceFile("/services/internal.yaml"); - return convertToDockerComposeService(composeText, name); - } - - public DockerComposeService convertToDockerComposeService(String code, String name) { - Yaml yaml = new Yaml(); - Map<String, Object> obj = yaml.load(code); - JsonObject json = JsonObject.mapFrom(obj); - JsonObject services = json.getJsonObject("services"); - if (services.containsKey(name)) { - JsonObject service = services.getJsonObject(name); - return convertToDockerComposeService(name, service); - } else { - Optional<JsonObject> j = services.fieldNames().stream() - .map(services::getJsonObject) - .filter(s -> s.getString("container_name").equalsIgnoreCase(name)).findFirst(); - if (j.isPresent()) { - return convertToDockerComposeService(name, j.get()); - } - } - return null; - } - - public DockerComposeService convertToDockerComposeService(String name, JsonObject service) { - if (service.containsKey(ENVIRONMENT) && service.getValue(ENVIRONMENT) instanceof JsonArray) { - JsonObject env = new JsonObject(); - service.getJsonArray(ENVIRONMENT).forEach(o -> { - String[] kv = o.toString().split("="); - env.put(kv[0], kv[1]); - }); - service.put(ENVIRONMENT, env); - } - DockerComposeService ds = service.mapTo(DockerComposeService.class); - if (ds.getContainer_name() == null) { - ds.setContainer_name(name); - } - return ds; - } - protected HealthCheck getHealthCheck(HealthCheckConfig config) { if (config != null) { HealthCheck healthCheck = new HealthCheck().withTest(config.getTest()); @@ -151,29 +109,16 @@ public class DockerServiceUtils { return Duration.parse(s).toMillis() * 1000000L; } - protected Ports getPortBindings(String ports, List<ExposedPort> exposedPorts, boolean inRange) { + protected Ports getPortBindings(Map<Integer, Integer> ports) { Ports portBindings = new Ports(); - getPortsFromString(ports).forEach((hostPort, containerPort) -> { - Ports.Binding binding = (exposedPorts.stream().anyMatch(e -> e.getPort() == containerPort)) - ? (inRange ? Ports.Binding.bindPortRange(hostPort, hostPort + 1000) : Ports.Binding.bindPort(hostPort)) - : Ports.Binding.bindPort(hostPort); + ports.forEach((hostPort, containerPort) -> { + Ports.Binding binding = Ports.Binding.bindPort(hostPort); portBindings.bind(ExposedPort.tcp(containerPort), binding); }); return portBindings; } - protected Map<Integer, Integer> getPortsFromString(String ports) { - Map<Integer, Integer> p = new HashMap<>(); - if (ports != null && !ports.isEmpty()) { - Arrays.stream(ports.split(",")).forEach(s -> { - String[] values = s.split(":"); - p.put(Integer.parseInt(values[0]), Integer.parseInt(values[1])); - }); - } - return p; - } - protected String formatMemory(Long memory) { try { if (memory < (1073741824)) { diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/model/DockerComposeService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/model/DockerComposeService.java index a348c706..629153e4 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/model/DockerComposeService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/model/DockerComposeService.java @@ -1,9 +1,6 @@ package org.apache.camel.karavan.docker.model; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; public class DockerComposeService { @@ -52,6 +49,17 @@ public class DockerComposeService { this.ports = ports; } + public Map<Integer, Integer> getPortsMap() { + Map<Integer, Integer> p = new HashMap<>(); + if (ports != null && !ports.isEmpty()) { + ports.forEach(s -> { + String[] values = s.split(":"); + p.put(Integer.parseInt(values[0]), Integer.parseInt(values[1])); + }); + } + return p; + } + public List<String> getExpose() { return expose; } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java index e76fcb30..73557d4d 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java @@ -27,6 +27,7 @@ import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.CamelStatus; import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.camel.karavan.kubernetes.KubernetesService; +import org.apache.camel.karavan.shared.ConfigService; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.faulttolerance.CircuitBreaker; import org.jboss.logging.Logger; @@ -40,8 +41,7 @@ import java.util.concurrent.ExecutionException; import static org.apache.camel.karavan.shared.Constants.LABEL_PROJECT_ID; import static org.apache.camel.karavan.shared.Constants.RELOAD_TRY_COUNT; -import static org.apache.camel.karavan.shared.EventType.CONTAINER_STATUS; -import static org.apache.camel.karavan.shared.EventType.DEVMODE_CONTAINER_READY; +import static org.apache.camel.karavan.shared.EventType.*; @ApplicationScoped public class CamelService { @@ -74,6 +74,37 @@ public class CamelService { return webClient; } + + public void loadCodeToDevMode(String projectId) { + LOGGER.info("DevMode reload code " + projectId); + ContainerStatus status = infinispanService.getContainerStatus(projectId, environment, projectId); + CamelStatus cs = infinispanService.getCamelStatus(projectId, environment, CamelStatus.Name.context.name()); + if (status != null + && !Objects.equals(status.getCodeLoaded(), Boolean.TRUE) + && status.getContainerId() != null + && status.getState().equals(ContainerStatus.State.running.name()) + && camelIsStarted(cs)) { + LOGGER.info("CAMEL STARTED -> SEND RELOAD"); + if (ConfigService.inKubernetes()) { + reloadProjectCode(projectId); + } else { + infinispanService.sendCodeReloadCommand(projectId); + } + } else { + + } + } + + private boolean camelIsStarted(CamelStatus camelStatus) { + try { + String status = camelStatus.getStatus(); + JsonObject obj = new JsonObject(status); + return Objects.equals("Started", obj.getJsonObject("context").getString("state")); + } catch (Exception e) { + return false; + } + } + public void reloadProjectCode(String projectId) { LOGGER.info("Reload project code " + projectId); try { diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java index f961d968..4e243bc2 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java @@ -23,12 +23,16 @@ import io.apicurio.datamodels.openapi.models.OasDocument; import io.quarkus.qute.Engine; import io.quarkus.qute.Template; import io.quarkus.qute.TemplateInstance; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.apache.camel.CamelContext; import org.apache.camel.generator.openapi.RestDslGenerator; import org.apache.camel.impl.DefaultCamelContext; import org.apache.camel.karavan.api.KameletResources; +import org.apache.camel.karavan.docker.DockerService; +import org.apache.camel.karavan.docker.model.DockerComposeService; import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.GitRepo; import org.apache.camel.karavan.infinispan.model.GitRepoFile; @@ -55,10 +59,18 @@ public class CodeService { private static final Logger LOGGER = Logger.getLogger(CodeService.class.getName()); public static final String APPLICATION_PROPERTIES_FILENAME = "application.properties"; public static final String DEV_SERVICES_FILENAME = "devservices.yaml"; + public static final String PROJECT_COMPOSE_FILENAME = "project-compose.yaml"; + private static final String SNIPPETS_PATH = "/snippets/"; + private static final int INTERNAL_PORT = 8080; + + protected static final String ENVIRONMENT = "environment"; @Inject KubernetesService kubernetesService; + @Inject + DockerService dockerService; + @Inject InfinispanService infinispanService; @@ -106,20 +118,22 @@ public class CodeService { return null; } - public Map<String, String> getApplicationPropertiesTemplates() { + public Map<String, String> getTemplates() { Map<String, String> result = new HashMap<>(); List<String> files = new ArrayList<>(interfaces); - files.addAll(targets.stream().map(target -> target + "-" + APPLICATION_PROPERTIES_FILENAME).collect(Collectors.toList())); + files.addAll(targets.stream().map(target -> target + "-" + APPLICATION_PROPERTIES_FILENAME).toList()); runtimes.forEach(runtime -> { files.forEach(file -> { String templateName = runtime + "-" + file; - String templatePath = "/snippets/" + templateName; + String templatePath = SNIPPETS_PATH + templateName; String templateText = getResourceFile(templatePath); result.put(templateName, templateText); }); }); + + result.put(PROJECT_COMPOSE_FILENAME, getResourceFile(SNIPPETS_PATH + PROJECT_COMPOSE_FILENAME)); return result; } @@ -254,4 +268,81 @@ public class CodeService { public String getProjectRuntime(String file) { return getProperty(file, "camel.jbang.runtime"); } + + public ProjectFile createInitialProjectCompose(Project project) { + int port = getNextAvailablePort(); + String templateText = getTemplateText(PROJECT_COMPOSE_FILENAME); + Template result = engine.parse(templateText); + TemplateInstance instance = result + .data("projectId", project.getProjectId()) + .data("projectPort", port) + .data("projectImage", project.getProjectId()); + String code = instance.render(); + return new ProjectFile(PROJECT_COMPOSE_FILENAME, code, project.getProjectId(), Instant.now().toEpochMilli()); + } + + private int getNextAvailablePort() { + int dockerPort = dockerService.getMaxPortMapped(INTERNAL_PORT); + int projectPort = getMaxPortMappedInProjects(); + return Math.max(projectPort, dockerPort) + 1; + } + + + private int getMaxPortMappedInProjects() { + List<ProjectFile> files = infinispanService.getProjectFilesByName(PROJECT_COMPOSE_FILENAME).stream() + .filter(f -> !Objects.equals(f.getProjectId(), Project.Type.templates.name())).toList(); + if (!files.isEmpty()) { + return files.stream().map(f -> convertToDockerComposeService(f.getCode(), f.getProjectId())) + .map(dcs -> { + Optional<Integer> port = dcs.getPortsMap().entrySet().stream() + .filter(e -> Objects.equals(e.getValue(), INTERNAL_PORT)).map(Map.Entry::getKey).findFirst(); + return port.orElse(INTERNAL_PORT); + }) + .mapToInt(Integer::intValue) + .max().orElse(INTERNAL_PORT); + } else { + return INTERNAL_PORT; + } + } + + + public DockerComposeService getInternalDockerComposeService (String name) { + String composeText = getResourceFile("/services/internal.yaml"); + return convertToDockerComposeService(composeText, name); + } + + public DockerComposeService convertToDockerComposeService(String code, String name) { + Yaml yaml = new Yaml(); + Map<String, Object> obj = yaml.load(code); + JsonObject json = JsonObject.mapFrom(obj); + JsonObject services = json.getJsonObject("services"); + if (services.containsKey(name)) { + JsonObject service = services.getJsonObject(name); + return convertToDockerComposeService(name, service); + } else { + Optional<JsonObject> j = services.fieldNames().stream() + .map(services::getJsonObject) + .filter(s -> s.getString("container_name").equalsIgnoreCase(name)).findFirst(); + if (j.isPresent()) { + return convertToDockerComposeService(name, j.get()); + } + } + return null; + } + + public DockerComposeService convertToDockerComposeService(String name, JsonObject service) { + if (service.containsKey(ENVIRONMENT) && service.getValue(ENVIRONMENT) instanceof JsonArray) { + JsonObject env = new JsonObject(); + service.getJsonArray(ENVIRONMENT).forEach(o -> { + String[] kv = o.toString().split("="); + env.put(kv[0], kv[1]); + }); + service.put(ENVIRONMENT, env); + } + DockerComposeService ds = service.mapTo(DockerComposeService.class); + if (ds.getContainer_name() == null) { + ds.setContainer_name(name); + } + return ds; + } } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/DelayRoute.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/DelayRoute.java deleted file mode 100644 index 8e594246..00000000 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/DelayRoute.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.apache.camel.karavan.service; - -import org.apache.camel.builder.endpoint.EndpointRouteBuilder; -import org.apache.camel.karavan.infinispan.InfinispanService; - -import static org.apache.camel.karavan.shared.EventType.*; - -public class DelayRoute extends EndpointRouteBuilder { - - @Override - public void configure() throws Exception { - from(vertx(DEVMODE_DELAY_MESSAGE)) - .delay(500) - .to(vertx(DEVMODE_CONTAINER_READY)); - -// from(vertx(InfinispanService.INFINISPAN_START_DELAY)) -// .delay(1000) -// .toD(vertx(InfinispanService.INFINISPAN_START)); - } -} diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java index 326946dd..e873d25f 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java @@ -8,16 +8,13 @@ import jakarta.inject.Inject; import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.CamelStatus; import org.apache.camel.karavan.infinispan.model.ContainerStatus; -import org.apache.camel.karavan.shared.ConfigService; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; -import java.util.Map; import java.util.Objects; -import static org.apache.camel.karavan.shared.Constants.LABEL_PROJECT_ID; -import static org.apache.camel.karavan.shared.Constants.RELOAD_TRY_COUNT; -import static org.apache.camel.karavan.shared.EventType.*; +import static org.apache.camel.karavan.shared.EventType.CONTAINER_STATUS; +import static org.apache.camel.karavan.shared.EventType.DEVMODE_CONTAINER_READY; @ApplicationScoped public class EventService { @@ -38,31 +35,31 @@ public class EventService { @ConsumeEvent(value = DEVMODE_CONTAINER_READY, blocking = true, ordered = true) void receiveCommand(JsonObject json) { - String projectId = json.getString(LABEL_PROJECT_ID); - Integer reloadCount = json.getInteger(RELOAD_TRY_COUNT); - LOGGER.info("DEVMODE_CONTAINER_READY " + projectId + " : " + reloadCount); - ContainerStatus status = infinispanService.getContainerStatus(projectId, environment, projectId); - CamelStatus cs = infinispanService.getCamelStatus(projectId, environment, CamelStatus.Name.context.name()); - if (status != null - && !Objects.equals(status.getCodeLoaded(), Boolean.TRUE) - && status.getContainerId() != null - && status.getState().equals(ContainerStatus.State.running.name()) - && camelIsStarted(cs)) { - LOGGER.info("CAMEL STARTED -> SEND RELOAD"); - if (ConfigService.inKubernetes()) { - camelService.reloadProjectCode(projectId); - } else { - infinispanService.sendCodeReloadCommand(projectId); - } - } else if (reloadCount < 30) { - LOGGER.info("CAMEL NOT STARTED -> SEND DEVMODE_CONTAINER_READY"); - // retry again - Map<String, Object> message = Map.of( - LABEL_PROJECT_ID, projectId, - RELOAD_TRY_COUNT, ++reloadCount - ); - eventBus.publish(DEVMODE_DELAY_MESSAGE, JsonObject.mapFrom(message)); - } +// String projectId = json.getString(LABEL_PROJECT_ID); +// Integer reloadCount = json.getInteger(RELOAD_TRY_COUNT); +// LOGGER.info("DEVMODE_CONTAINER_READY " + projectId + " : " + reloadCount); +// ContainerStatus status = infinispanService.getContainerStatus(projectId, environment, projectId); +// CamelStatus cs = infinispanService.getCamelStatus(projectId, environment, CamelStatus.Name.context.name()); +// if (status != null +// && !Objects.equals(status.getCodeLoaded(), Boolean.TRUE) +// && status.getContainerId() != null +// && status.getState().equals(ContainerStatus.State.running.name()) +// && camelIsStarted(cs)) { +// LOGGER.info("CAMEL STARTED -> SEND RELOAD"); +// if (ConfigService.inKubernetes()) { +// camelService.reloadProjectCode(projectId); +// } else { +// infinispanService.sendCodeReloadCommand(projectId); +// } +// } else if (reloadCount < 30) { +// LOGGER.info("CAMEL NOT STARTED -> SEND DEVMODE_CONTAINER_READY"); +// // retry again +// Map<String, Object> message = Map.of( +// LABEL_PROJECT_ID, projectId, +// RELOAD_TRY_COUNT, ++reloadCount +// ); +// eventBus.publish(DEVMODE_DELAY_MESSAGE, JsonObject.mapFrom(message)); +// } } private boolean camelIsStarted(CamelStatus camelStatus) { @@ -75,6 +72,8 @@ public class EventService { } } + + @ConsumeEvent(value = CONTAINER_STATUS, blocking = true, ordered = true) public void saveContainerStatus(JsonObject data) { if (infinispanService.isReady()) { diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java index 7e91bed2..6a5304a9 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java @@ -93,7 +93,7 @@ public class KaravanService { dockerService.startListeners(); dockerForInfinispan.startInfinispan(); - dockerForKaravan.startKaravanHeadlessContainer(); +// dockerForKaravan.startKaravanHeadlessContainer(); if (giteaInstall) { dockerForGitea.startGitea(); giteaService.install(); diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java index 7dbdd75a..1ec31c08 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java @@ -17,17 +17,25 @@ package org.apache.camel.karavan.service; import io.smallrye.mutiny.tuples.Tuple2; +import io.vertx.core.json.JsonObject; +import io.vertx.mutiny.core.eventbus.EventBus; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.core.Response; +import org.apache.camel.karavan.docker.DockerForKaravan; +import org.apache.camel.karavan.docker.model.DockerComposeService; import org.apache.camel.karavan.infinispan.InfinispanService; +import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.camel.karavan.infinispan.model.GitRepo; import org.apache.camel.karavan.infinispan.model.Project; import org.apache.camel.karavan.infinispan.model.ProjectFile; import org.apache.camel.karavan.kubernetes.KubernetesService; +import org.apache.camel.karavan.shared.ConfigService; +import org.apache.camel.karavan.shared.EventType; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.faulttolerance.Retry; import org.eclipse.microprofile.health.HealthCheck; import org.eclipse.microprofile.health.HealthCheckResponse; -import org.eclipse.microprofile.health.HealthCheckResponseBuilder; import org.eclipse.microprofile.health.Readiness; import org.jboss.logging.Logger; @@ -36,11 +44,14 @@ import jakarta.enterprise.inject.Default; import jakarta.inject.Inject; import java.time.Instant; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; import static org.apache.camel.karavan.service.CodeService.DEV_SERVICES_FILENAME; +import static org.apache.camel.karavan.service.CodeService.PROJECT_COMPOSE_FILENAME; @Default @Readiness @@ -49,6 +60,9 @@ public class ProjectService implements HealthCheck{ private static final Logger LOGGER = Logger.getLogger(ProjectService.class.getName()); + @ConfigProperty(name = "karavan.environment") + String environment; + @ConfigProperty(name = "karavan.git-pull-interval") String gitPullInterval; @@ -58,12 +72,18 @@ public class ProjectService implements HealthCheck{ @Inject KubernetesService kubernetesService; + @Inject + DockerForKaravan dockerForKaravan; + @Inject GitService gitService; @Inject CodeService codeService; + @Inject + EventBus eventBus; + private AtomicBoolean ready = new AtomicBoolean(false); private AtomicBoolean readyToPull = new AtomicBoolean(false); @@ -77,6 +97,47 @@ public class ProjectService implements HealthCheck{ } } + public String runProjectWithJBangOptions(Project project, @PathParam("jBangOptions") String jBangOptions) throws Exception { + String containerName = project.getProjectId(); + ContainerStatus status = infinispanService.getDevModeContainerStatus(project.getProjectId(), environment); + if (status == null) { + status = ContainerStatus.createDevMode(project.getProjectId(), environment); + } + + if (!Objects.equals(status.getState(), ContainerStatus.State.running.name())) { + status.setInTransit(true); + eventBus.send(EventType.CONTAINER_STATUS, JsonObject.mapFrom(status)); + + if (ConfigService.inKubernetes()) { + kubernetesService.runDevModeContainer(project, jBangOptions); + } else { + Map<String, String> files = infinispanService.getProjectFiles(project.getProjectId()).stream() + .filter(f -> !Objects.equals(f.getName(), PROJECT_COMPOSE_FILENAME)) + .collect(Collectors.toMap(ProjectFile::getName, ProjectFile::getCode)); + ProjectFile compose = infinispanService.getProjectFile(project.getProjectId(), PROJECT_COMPOSE_FILENAME); + DockerComposeService dcs = codeService.convertToDockerComposeService(compose.getCode(), project.getProjectId()); + dockerForKaravan.runProjectInDevMode(project.getProjectId(), jBangOptions, dcs.getPortsMap(), files); + } + return containerName; + } else { + return null; + } + } + + public Project save(Project project) throws Exception { + boolean isNew = infinispanService.getProject(project.getProjectId()) == null; + infinispanService.saveProject(project); + if (isNew){ + ProjectFile appProp = codeService.getApplicationProperties(project); + infinispanService.saveProjectFile(appProp); + if (!ConfigService.inKubernetes()) { + ProjectFile projectCompose = codeService.createInitialProjectCompose(project); + infinispanService.saveProjectFile(projectCompose); + } + } + return project; + } + public void pullCommits() { if (readyToPull.get()) { LOGGER.info("Pull commits..."); @@ -224,7 +285,7 @@ public class ProjectService implements HealthCheck{ templates = new Project(Project.Type.templates.name(), "Templates", "Templates", "", "", Instant.now().toEpochMilli(), Project.Type.templates); infinispanService.saveProject(templates); - codeService.getApplicationPropertiesTemplates().forEach((name, value) -> { + codeService.getTemplates().forEach((name, value) -> { ProjectFile file = new ProjectFile(name, value, Project.Type.templates.name(), Instant.now().toEpochMilli()); infinispanService.saveProjectFile(file); }); @@ -262,7 +323,7 @@ public class ProjectService implements HealthCheck{ pipelines = new Project(Project.Type.pipelines.name(), "Pipelines", "CI/CD Pipelines", "", "", Instant.now().toEpochMilli(), Project.Type.pipelines); infinispanService.saveProject(pipelines); - codeService.getApplicationPropertiesTemplates().forEach((name, value) -> { + codeService.getTemplates().forEach((name, value) -> { ProjectFile file = new ProjectFile(name, value, Project.Type.pipelines.name(), Instant.now().toEpochMilli()); infinispanService.saveProjectFile(file); }); diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java index 0f6ba504..480185ab 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java @@ -19,7 +19,6 @@ package org.apache.camel.karavan.shared; public class EventType { public static final String CONTAINER_STATUS = "CONTAINER_STATUS"; - public static final String DEVMODE_CONTAINER_READY = "DEVMODE_STATUS"; - public static final String DEVMODE_DELAY_MESSAGE = "DEVMODE_DELAY_MESSAGE"; + public static final String DEVMODE_CONTAINER_READY = "DEVMODE_CONTAINER_READY"; } diff --git a/karavan-web/karavan-app/src/main/resources/snippets/project-compose.yaml b/karavan-web/karavan-app/src/main/resources/snippets/project-compose.yaml new file mode 100644 index 00000000..36c668af --- /dev/null +++ b/karavan-web/karavan-app/src/main/resources/snippets/project-compose.yaml @@ -0,0 +1,8 @@ +services: + {projectId}: + image: {projectImage} + restart: always + ports: + - "{projectPort}:8080" + networks: + - karavan diff --git a/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx index 9237fba3..a8ab3c75 100644 --- a/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx @@ -257,8 +257,8 @@ export class KaravanApi { }); } - static async getFiles(project: string, after: (files: ProjectFile[]) => void) { - instance.get('/api/file/' + project) + static async getFiles(projectId: string, after: (files: ProjectFile[]) => void) { + instance.get('/api/file/' + projectId) .then(res => { if (res.status === 200) { after(res.data); diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts index 9ebc6eda..7bef52d9 100644 --- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts +++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts @@ -111,7 +111,7 @@ export class ProjectService { if (res.status === 200 || res.status === 201) { useProjectStore.setState({isPushing: false}) ProjectService.refreshProject(project.projectId); - ProjectService.refreshProjectData(); + ProjectService.refreshProjectData(project.projectId); } else { // Todo notification } @@ -169,7 +169,7 @@ export class ProjectService { KaravanApi.deleteProject(project, res => { if (res.status === 204) { // this.props.toast?.call(this, 'Success', 'Project deleted', 'success'); - ProjectService.refreshProjectData(); + ProjectService.refreshProjectData(project.projectId); } else { // this.props.toast?.call(this, 'Error', res.statusText, 'danger'); } @@ -179,7 +179,7 @@ export class ProjectService { public static createProject(project: Project) { KaravanApi.postProject(project, res => { if (res.status === 200 || res.status === 201) { - ProjectService.refreshProjectData(); + ProjectService.refreshProjectData(project.projectId); // this.props.toast?.call(this, 'Success', 'Project created', 'success'); } else { // this.props.toast?.call(this, 'Error', res.status + ', ' + res.statusText, 'danger'); @@ -191,7 +191,7 @@ export class ProjectService { KaravanApi.postProjectFile(file, res => { if (res.status === 200) { // console.log(res) //TODO show notification - ProjectService.refreshProjectData(); + ProjectService.refreshProjectData(file.projectId); } else { // console.log(res) //TODO show notification } @@ -201,15 +201,14 @@ export class ProjectService { public static deleteFile(file: ProjectFile) { KaravanApi.deleteProjectFile(file, res => { if (res.status === 204) { - ProjectService.refreshProjectData(); + ProjectService.refreshProjectData(file.projectId); } else { } }); } - public static refreshProjectData() { - const project = useProjectStore.getState().project; - KaravanApi.getProject(project.projectId, (project: Project) => { + public static refreshProjectData(projectId: string) { + KaravanApi.getProject(projectId, (project: Project) => { // ProjectEventBus.selectProject(project); KaravanApi.getTemplatesFiles((files: ProjectFile[]) => { files.filter(f => f.name.endsWith('java')) @@ -220,7 +219,7 @@ export class ProjectService { }) }); }); - KaravanApi.getFiles(project.projectId, (files: ProjectFile[]) => { + KaravanApi.getFiles(projectId, (files: ProjectFile[]) => { useFilesStore.setState({files: files}); }); diff --git a/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx b/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx index 6ff24777..8d7de7ab 100644 --- a/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx @@ -33,12 +33,10 @@ export function MainDataPoller () { }, [project, readiness]); function getData() { - console.log(readiness); KaravanApi.getReadiness((r: any) => { setReadiness(r); }) if (readiness) { - console.log("getData"); setLoading(true); KaravanApi.getConfiguration((config: AppConfig) => { if (project.projectId === undefined) { diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx index 2240c8a8..94fb1d28 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx @@ -19,10 +19,12 @@ export function ProjectPanel () { useEffect(() => { onRefresh(); - }); + }, [project]); function onRefresh () { - ProjectService.refreshProjectData(); + if (project.projectId) { + ProjectService.refreshProjectData(project.projectId); + } } function isBuildIn(): boolean { diff --git a/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx index 6c444bf5..2e2d0bd8 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useEffect} from 'react'; import { Badge, Button, @@ -115,7 +115,7 @@ export function FilesTab () { <Td modifier={"fitContent"}> {canDeleteFiles() && <Button style={{padding: '0'}} variant={"plain"} - isDisabled={file.name === 'application.properties'} + isDisabled={['application.properties', 'project-compose.yaml'].includes(file.name)} onClick={e => useFileStore.setState({file: file, operation: "delete"}) }> diff --git a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java index 3ea2c584..eae0311f 100644 --- a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java +++ b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java @@ -196,6 +196,13 @@ public class InfinispanService implements HealthCheck { return list.size() > 0 ? list.get(0) : null; } + public List<ProjectFile> getProjectFilesByName(String filename) { + QueryFactory queryFactory = Search.getQueryFactory(files); + return queryFactory.<ProjectFile>create("FROM karavan.ProjectFile WHERE name = :name") + .setParameter("name", filename) + .execute().list(); + } + public void saveProjectFile(ProjectFile file) { files.put(GroupedKey.create(file.getProjectId(), DEFAULT_ENVIRONMENT, file.getName()), file); }