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 5eafe2958b74831443eb3e552574953657e43a8f Author: Marat Gubaidullin <marat.gubaidul...@gmail.com> AuthorDate: Tue Jul 25 17:27:28 2023 -0400 Container management improvements #817 --- .../apache/camel/karavan/api/DevModeResource.java | 18 ++-- .../camel/karavan/api/InfrastructureResource.java | 62 +++++++++-- .../camel/karavan/docker/DockerEventListener.java | 111 +++++++++++--------- .../apache/camel/karavan/docker/DockerService.java | 113 ++++++++++++++++----- .../karavan/kubernetes/KubernetesService.java | 2 +- .../camel/karavan/kubernetes/PodEventHandler.java | 28 +++-- .../apache/camel/karavan/service/CamelService.java | 6 +- .../apache/camel/karavan/service/EventService.java | 41 ++++---- .../camel/karavan/service/ScheduledService.java | 32 +++++- .../org/apache/camel/karavan/shared/EventType.java | 2 +- .../src/main/resources/application.properties | 2 +- .../src/main/webui/src/api/KaravanApi.tsx | 23 +++-- .../src/main/webui/src/api/ProjectModels.ts | 3 + .../src/main/webui/src/api/ProjectService.ts | 49 ++++++--- .../src/main/webui/src/api/ProjectStore.ts | 6 +- .../webui/src/containers/ContainerTableRow.tsx | 71 +++++++++---- .../main/webui/src/containers/ContainersPage.tsx | 35 ++++--- .../src/main/webui/src/project/DevModeToolbar.tsx | 92 ++++++++--------- .../src/main/webui/src/project/ProjectPage.tsx | 2 +- .../webui/src/project/dashboard/InfoContainer.tsx | 4 +- .../webui/src/project/pipeline/ProjectStatus.tsx | 2 +- .../main/webui/src/services/ServicesTableRow.tsx | 4 +- .../camel/karavan/headless/CamelService.java | 4 +- .../camel/karavan/headless/HeadlessService.java | 3 +- .../karavan/infinispan/InfinispanService.java | 24 ++++- .../karavan/infinispan/model/ContainerStatus.java | 104 +++++++++++++------ .../karavan/infinispan/model/KaravanSchema.java | 4 +- 27 files changed, 572 insertions(+), 275 deletions(-) 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 13edee0e..6379b058 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 @@ -30,6 +30,7 @@ import javax.inject.Inject; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import java.util.Objects; import java.util.Optional; @Path("/api/devmode") @@ -58,7 +59,13 @@ public class DevModeResource { String containerName = project.getProjectId(); ContainerStatus status = infinispanService.getDevModeContainerStatus(project.getProjectId(), environment); if (status == null) { - infinispanService.saveContainerStatus(ContainerStatus.createDevMode(project.getProjectId(), environment)); + status = ContainerStatus.createDevMode(project.getProjectId(), environment); + } + + if (!Objects.equals(status.getState(), ContainerStatus.State.running.name())){ + status.setInTransit(true); + infinispanService.saveContainerStatus(status); + if (ConfigService.inKubernetes()) { kubernetesService.runDevModeContainer(project, ""); } else { @@ -93,15 +100,10 @@ public class DevModeResource { @Consumes(MediaType.APPLICATION_JSON) @Path("/{projectId}/{deletePVC}") public Response deleteDevMode(@PathParam("projectId") String projectId, @PathParam("deletePVC") boolean deletePVC) { - ContainerStatus status = infinispanService.getDevModeContainerStatus(projectId, environment); - if (status != null) { - status.setLifeCycle(ContainerStatus.Lifecycle.deleting); - infinispanService.saveContainerStatus(status); - } + infinispanService.setContainerStatusTransit(projectId, environment, projectId); if (ConfigService.inKubernetes()) { kubernetesService.deleteRunner(projectId, deletePVC); } else { - dockerService.stopContainer(projectId); dockerService.deleteContainer(projectId); } return Response.accepted().build(); @@ -109,7 +111,7 @@ public class DevModeResource { @GET @Produces(MediaType.APPLICATION_JSON) - @Path("/pod/{projectId}") + @Path("/container/{projectId}") public Response getPodStatus(@PathParam("projectId") String projectId) throws RuntimeException { if (infinispanService.isReady()) { ContainerStatus cs = infinispanService.getDevModeContainerStatus(projectId, environment); 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 443c9e69..8291c7eb 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 @@ -17,8 +17,10 @@ package org.apache.camel.karavan.api; import io.smallrye.mutiny.Multi; +import io.vertx.core.json.JsonObject; import io.vertx.mutiny.core.eventbus.EventBus; import io.vertx.mutiny.core.eventbus.Message; +import org.apache.camel.karavan.docker.DockerService; import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.camel.karavan.infinispan.model.DeploymentStatus; @@ -50,6 +52,9 @@ public class InfrastructureResource { @Inject KubernetesService kubernetesService; + @Inject + DockerService dockerService; + @ConfigProperty(name = "karavan.environment") String environment; @@ -101,9 +106,13 @@ public class InfrastructureResource { @Produces(MediaType.APPLICATION_JSON) @Path("/deployment") public List<DeploymentStatus> getAllDeploymentStatuses() throws Exception { - return infinispanService.getDeploymentStatuses().stream() - .sorted(Comparator.comparing(DeploymentStatus::getProjectId)) - .collect(Collectors.toList()); + if (infinispanService.isReady()) { + return infinispanService.getDeploymentStatuses().stream() + .sorted(Comparator.comparing(DeploymentStatus::getProjectId)) + .collect(Collectors.toList()); + } else { + return List.of(); + } } @GET @@ -154,9 +163,29 @@ public class InfrastructureResource { } } + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/container/{env}/{name}") + public Response startContainer(@PathParam("env") String env, @PathParam("name") String name, JsonObject command) throws Exception { + if (infinispanService.isReady()) { + infinispanService.setContainerStatusTransit(name, env, name); + if (command.containsKey("command")) { + if (command.getString("command").equalsIgnoreCase("start")) { + dockerService.startContainer(name); + return Response.ok().build(); + } else if (command.getString("command").equalsIgnoreCase("stop")) { + dockerService.stopContainer(name); + return Response.ok().build(); + } + } + } + return Response.notModified().build(); + } + @GET @Produces(MediaType.APPLICATION_JSON) - @Path("/pod/{env}") + @Path("/container/{env}") public List<ContainerStatus> getContainerStatusesByEnv(@PathParam("env") String env) throws Exception { return infinispanService.getContainerStatuses(env).stream() .sorted(Comparator.comparing(ContainerStatus::getProjectId)) @@ -165,10 +194,10 @@ public class InfrastructureResource { @GET @Produces(MediaType.APPLICATION_JSON) - @Path("/pod/{projectId}/{env}") + @Path("/container/{projectId}/{env}") public List<ContainerStatus> getContainerStatusesByProjectAndEnv(@PathParam("projectId") String projectId, @PathParam("env") String env) throws Exception { return infinispanService.getContainerStatuses(projectId, env).stream() - .filter(podStatus -> Objects.equals(podStatus.getType(), ContainerStatus.CType.project)) + .filter(podStatus -> Objects.equals(podStatus.getType(), ContainerStatus.ContainerType.project)) .sorted(Comparator.comparing(ContainerStatus::getContainerName)) .collect(Collectors.toList()); } @@ -176,10 +205,23 @@ public class InfrastructureResource { @DELETE @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Path("/pod/{env}/{name}") - public Response deleteContainer(@PathParam("env") String env, @PathParam("name") String name) throws Exception { - kubernetesService.deletePod(name, kubernetesService.getNamespace()); - return Response.accepted().build(); + @Path("/container/{env}/{name}") + public Response deleteContainer(@PathParam("env") String env, @PathParam("name") String name) { + if (infinispanService.isReady()) { + infinispanService.setContainerStatusTransit(name, env, name); + try { + if (ConfigService.inKubernetes()) { + kubernetesService.deletePod(name, kubernetesService.getNamespace()); + } else { + dockerService.deleteContainer(name); + } + return Response.accepted().build(); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + return Response.notModified().build(); + } + } + return Response.notModified().build(); } @GET 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 b055716d..86c40428 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 @@ -17,12 +17,10 @@ import javax.inject.Inject; import java.io.Closeable; import java.io.IOException; import java.time.Instant; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; +import static org.apache.camel.karavan.docker.DockerService.LABEL_PROJECT_ID; import static org.apache.camel.karavan.docker.DockerService.LABEL_TYPE; import static org.apache.camel.karavan.shared.EventType.DEVMODE_CONTAINER_READY; import static org.apache.camel.karavan.shared.EventType.INFINISPAN_STARTED; @@ -63,24 +61,22 @@ public class DockerEventListener implements ResultCallback<Event> { public void onContainerEvent(Event event, Container container) { if (infinispanService.isReady()) { - if (Arrays.asList("destroy", "stop", "die", "kill", "pause", "destroy", "rename").contains(event.getStatus())) { - onDeleteContainer(container); - } else if (Arrays.asList("create", "start", "unpause").contains(event.getStatus())) { - onCreateContainer(container, event); - } else { +// if (Arrays.asList("create", "start", "unpause", "stop", "pause").contains(event.getStatus())) { +// onExistingContainer(container); +// } else { String status = event.getStatus(); if (status.startsWith("health_status:")) { if (container.getNames()[0].equals("/infinispan")) { onInfinispanHealthEvent(container, event); } else if (inDevMode(container)) { - onDevModeHealthEvent(container,event); + onDevModeHealthEvent(container, event); } } - } +// } } } - private void onDeleteContainer(Container container){ + public void onDeletedContainer(Container container) { String name = container.getNames()[0].replace("/", ""); infinispanService.deleteContainerStatus(name, environment, name); if (inDevMode(container)) { @@ -88,24 +84,27 @@ public class DockerEventListener implements ResultCallback<Event> { } } - protected void onCreateContainer(Container container, Event event){ - String name = container.getNames()[0].replace("/", ""); - List<Integer> ports = Arrays.stream(container.getPorts()).map(ContainerPort::getPrivatePort).filter(Objects::nonNull).collect(Collectors.toList()); - ContainerStatus.Lifecycle lc = event.getStatus().equals("create") ? ContainerStatus.Lifecycle.init : ContainerStatus.Lifecycle.ready; - ContainerStatus.CType type = getCtype(container.getLabels()); - String created = Instant.ofEpochSecond(container.getCreated()).toString(); - ContainerStatus ci = infinispanService.getContainerStatus(name, environment, name); - if (ci == null) { - ci = ContainerStatus.createWithId(name, environment, container.getId(), container.getImage(), ports, type, lc, created); - } else { - ci.setContainerId(container.getId()); - ci.setPorts(ports); - ci.setType(type); - ci.setLifeCycle(lc); - ci.setCreated(created); - ci.setImage(container.getImage()); + protected void onExistingContainer(Container container) { + if (infinispanService.isReady()) { + String name = container.getNames()[0].replace("/", ""); + List<Integer> ports = Arrays.stream(container.getPorts()).map(ContainerPort::getPrivatePort).filter(Objects::nonNull).collect(Collectors.toList()); + List<ContainerStatus.Command> commands = getContainerCommand(container.getState()); + ContainerStatus.ContainerType type = getContainerType(container.getLabels()); + String created = Instant.ofEpochSecond(container.getCreated()).toString(); + ContainerStatus ci = infinispanService.getContainerStatus(name, environment, name); + if (ci == null) { + ci = ContainerStatus.createWithId(name, environment, container.getId(), container.getImage(), ports, type, commands, container.getState(), created); + } else { + ci.setContainerId(container.getId()); + ci.setPorts(ports); + ci.setType(type); + ci.setCommands(commands); + ci.setCreated(created); + ci.setState(container.getState()); + ci.setImage(container.getImage()); + } + infinispanService.saveContainerStatus(ci); } - infinispanService.saveContainerStatus(ci); } public void onInfinispanHealthEvent(Container container, Event event) { @@ -116,36 +115,50 @@ public class DockerEventListener implements ResultCallback<Event> { } public void onDevModeHealthEvent(Container container, Event event) { - String name = container.getNames()[0].replace("/", ""); String status = event.getStatus(); String health = status.replace("health_status: ", ""); LOGGER.infof("Container %s health status: %s", container.getNames()[0], health); - // update ContainerStatus: set ready and - ContainerStatus cs = infinispanService.getDevModeContainerStatus(name, environment); - if (cs != null) { - cs.setLifeCycle(ContainerStatus.Lifecycle.ready); - cs.setContainerId(container.getId()); - infinispanService.saveContainerStatus(cs); - eventBus.publish(DEVMODE_CONTAINER_READY, JsonObject.mapFrom(cs)); - } + eventBus.publish(DEVMODE_CONTAINER_READY, container.getLabels().get(LABEL_PROJECT_ID)); } private boolean inDevMode(Container container) { - return Objects.equals(getCtype(container.getLabels()), ContainerStatus.CType.devmode); + return Objects.equals(getContainerType(container.getLabels()), ContainerStatus.ContainerType.devmode); } - private ContainerStatus.CType getCtype(Map<String, String> labels) { + private ContainerStatus.ContainerType getContainerType(Map<String, String> labels) { String type = labels.get(LABEL_TYPE); - if (Objects.equals(type, ContainerStatus.CType.devmode.name())) { - return ContainerStatus.CType.devmode; - } else if (Objects.equals(type, ContainerStatus.CType.devservice.name())) { - return ContainerStatus.CType.devservice; - } else if (Objects.equals(type, ContainerStatus.CType.project.name())) { - return ContainerStatus.CType.project; - } else if (Objects.equals(type, ContainerStatus.CType.internal.name())) { - return ContainerStatus.CType.internal; + if (Objects.equals(type, ContainerStatus.ContainerType.devmode.name())) { + return ContainerStatus.ContainerType.devmode; + } else if (Objects.equals(type, ContainerStatus.ContainerType.devservice.name())) { + return ContainerStatus.ContainerType.devservice; + } else if (Objects.equals(type, ContainerStatus.ContainerType.project.name())) { + return ContainerStatus.ContainerType.project; + } else if (Objects.equals(type, ContainerStatus.ContainerType.internal.name())) { + return ContainerStatus.ContainerType.internal; + } + return ContainerStatus.ContainerType.unknown; + } + + private List<ContainerStatus.Command> getContainerCommand(String state) { + List<ContainerStatus.Command> result = new ArrayList<>(); + if (Objects.equals(state, ContainerStatus.State.created.name())) { + result.add(ContainerStatus.Command.run); + result.add(ContainerStatus.Command.delete); + } else if (Objects.equals(state, ContainerStatus.State.exited.name())) { + result.add(ContainerStatus.Command.run); + result.add(ContainerStatus.Command.delete); + } else if (Objects.equals(state, ContainerStatus.State.running.name())) { + result.add(ContainerStatus.Command.pause); + result.add(ContainerStatus.Command.stop); + result.add(ContainerStatus.Command.delete); + } else if (Objects.equals(state, ContainerStatus.State.paused.name())) { + result.add(ContainerStatus.Command.run); + result.add(ContainerStatus.Command.stop); + result.add(ContainerStatus.Command.delete); + } else if (Objects.equals(state, ContainerStatus.State.dead.name())) { + result.add(ContainerStatus.Command.delete); } - return ContainerStatus.CType.unknown; + return result; } @Override 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 27d5a6b3..84178d09 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 @@ -14,7 +14,7 @@ import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; import com.github.dockerjava.transport.DockerHttpClient; import io.smallrye.mutiny.tuples.Tuple2; import io.vertx.core.eventbus.EventBus; -import io.vertx.core.json.JsonObject; +import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.camel.karavan.infinispan.model.Project; import org.eclipse.microprofile.config.inject.ConfigProperty; @@ -24,6 +24,7 @@ import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import java.io.IOException; import java.text.DecimalFormat; +import java.time.Instant; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -47,6 +48,9 @@ public class DockerService { private static final Map<String, Tuple2<Long, Long>> previousStats = new ConcurrentHashMap<>(); private static final List<String> infinispanHealthCheckCMD = List.of("CMD", "curl", "-f", "http://localhost:11222/rest/v2/cache-managers/default/health/status"); + @ConfigProperty(name = "karavan.environment") + String environment; + @ConfigProperty(name = "karavan.devmode.image") String devmodeImage; @@ -67,17 +71,25 @@ public class DockerService { @Inject DockerEventListener dockerEventListener; + @Inject + InfinispanService infinispanService; + @Inject EventBus eventBus; + private DockerClient dockerClient; + public void runDevmodeContainer(Project project, String jBangOptions) throws InterruptedException { String projectId = project.getProjectId(); LOGGER.infof("DevMode starting for %s", projectId); + HealthCheck healthCheck = new HealthCheck().withTest(List.of("CMD", "curl", "-f", "http://localhost:8080/q/dev/health")) .withInterval(10000000000L).withTimeout(10000000000L).withStartPeriod(10000000000L).withRetries(30); + createContainer(projectId, devmodeImage, List.of(), null, false, false, healthCheck, - Map.of(LABEL_TYPE, ContainerStatus.CType.devmode.name(), LABEL_PROJECT_ID, projectId)); + Map.of(LABEL_TYPE, ContainerStatus.ContainerType.devmode.name(), LABEL_PROJECT_ID, projectId)); + startContainer(projectId); LOGGER.infof("DevMode started for %s", projectId); } @@ -91,8 +103,9 @@ public class DockerService { createContainer(INFINISPAN_CONTAINER_NAME, infinispanImage, List.of("USER=" + infinispanUsername, "PASS=" + infinispanPassword), - infinispanPort, false, true, healthCheck, Map.of() - ); + infinispanPort, false, true, healthCheck, + Map.of(LABEL_TYPE, ContainerStatus.ContainerType.internal.name())); + startContainer(INFINISPAN_CONTAINER_NAME); LOGGER.info("Infinispan is started"); } catch (Exception e) { @@ -110,7 +123,9 @@ public class DockerService { "INFINISPAN_USERNAME=" + infinispanUsername, "INFINISPAN_PASSWORD=" + infinispanPassword ), - null, false, false, new HealthCheck(), Map.of()); + null, false, false, new HealthCheck(), + Map.of(LABEL_TYPE, ContainerStatus.ContainerType.internal.name())); + startContainer(KARAVAN_CONTAINER_NAME); LOGGER.info("Karavan headless is started"); } catch (Exception e) { @@ -127,26 +142,33 @@ public class DockerService { } } - public void collectContainersStats() { - getDockerClient().listContainersCmd().exec().forEach(container -> { - Statistics stats = getContainerStats(container.getId()); - - String name = container.getNames()[0].replace("/", ""); - String memoryUsage = formatMemory(stats.getMemoryStats().getUsage()); - String memoryLimit = formatMemory(stats.getMemoryStats().getLimit()); - JsonObject data = JsonObject.of( - "projectId", name, - "memory", memoryUsage + " / " + memoryLimit, - "cpu", formatCpu(name, stats) - ); - eventBus.publish(CONTAINER_STATISTICS, data); + public List<ContainerStatus> collectContainersStatuses() { + List<ContainerStatus> result = new ArrayList<>(); + getDockerClient().listContainersCmd().withShowAll(true).exec().forEach(container -> { + ContainerStatus containerStatus = getContainerStatus(container); + updateStatistics(containerStatus, container); + result.add(containerStatus); }); + return result; } - public void collectContainersStatuses() { - getDockerClient().listContainersCmd().exec().forEach(container -> { - dockerEventListener.onCreateContainer(container, new Event(container.getStatus(), container.getId(), container.getImage(), container.getCreated())); - }); + private ContainerStatus getContainerStatus(Container container) { + String name = container.getNames()[0].replace("/", ""); + List<Integer> ports = Arrays.stream(container.getPorts()).map(ContainerPort::getPrivatePort).filter(Objects::nonNull).collect(Collectors.toList()); + List<ContainerStatus.Command> commands = getContainerCommand(container.getState()); + ContainerStatus.ContainerType type = getContainerType(container.getLabels()); + String created = Instant.ofEpochSecond(container.getCreated()).toString(); + return ContainerStatus.createWithId(name, environment, container.getId(), container.getImage(), ports, type, commands, container.getState(), created); + } + + private void updateStatistics(ContainerStatus containerStatus, Container container) { + Statistics stats = getContainerStats(container.getId()); + if (stats != null && stats.getMemoryStats() != null) { + String memoryUsage = formatMemory(stats.getMemoryStats().getUsage()); + String memoryLimit = formatMemory(stats.getMemoryStats().getLimit()); + containerStatus.setMemoryInfo(memoryUsage + " / " + memoryLimit); + containerStatus.setCpuInfo(formatCpu(containerStatus.getContainerName(), stats)); + } } public void startListeners() { @@ -276,10 +298,14 @@ public class DockerService { } public void deleteContainer(String name) { + long time = System.currentTimeMillis(); List<Container> containers = getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(name)).exec(); + System.out.println("Get containers " + (System.currentTimeMillis() - time)); + time = System.currentTimeMillis(); if (containers.size() == 1) { Container container = containers.get(0); - getDockerClient().removeContainerCmd(container.getId()).exec(); + getDockerClient().removeContainerCmd(container.getId()).withForce(true).exec(); + System.out.println("removeContainerCmd " + (System.currentTimeMillis() - time)); } } @@ -335,7 +361,10 @@ public class DockerService { } private DockerClient getDockerClient() { - return DockerClientImpl.getInstance(getDockerClientConfig(), getDockerHttpClient()); + if (dockerClient == null) { + dockerClient = DockerClientImpl.getInstance(getDockerClientConfig(), getDockerHttpClient()); + } + return dockerClient; } private String formatMemory(Long memory) { @@ -350,6 +379,42 @@ public class DockerService { } } + private ContainerStatus.ContainerType getContainerType(Map<String, String> labels) { + String type = labels.get(LABEL_TYPE); + if (Objects.equals(type, ContainerStatus.ContainerType.devmode.name())) { + return ContainerStatus.ContainerType.devmode; + } else if (Objects.equals(type, ContainerStatus.ContainerType.devservice.name())) { + return ContainerStatus.ContainerType.devservice; + } else if (Objects.equals(type, ContainerStatus.ContainerType.project.name())) { + return ContainerStatus.ContainerType.project; + } else if (Objects.equals(type, ContainerStatus.ContainerType.internal.name())) { + return ContainerStatus.ContainerType.internal; + } + return ContainerStatus.ContainerType.unknown; + } + + private List<ContainerStatus.Command> getContainerCommand(String state) { + List<ContainerStatus.Command> result = new ArrayList<>(); + if (Objects.equals(state, ContainerStatus.State.created.name())) { + result.add(ContainerStatus.Command.run); + result.add(ContainerStatus.Command.delete); + } else if (Objects.equals(state, ContainerStatus.State.exited.name())) { + result.add(ContainerStatus.Command.run); + result.add(ContainerStatus.Command.delete); + } else if (Objects.equals(state, ContainerStatus.State.running.name())) { + result.add(ContainerStatus.Command.pause); + result.add(ContainerStatus.Command.stop); + result.add(ContainerStatus.Command.delete); + } else if (Objects.equals(state, ContainerStatus.State.paused.name())) { + result.add(ContainerStatus.Command.run); + result.add(ContainerStatus.Command.stop); + result.add(ContainerStatus.Command.delete); + } else if (Objects.equals(state, ContainerStatus.State.dead.name())) { + result.add(ContainerStatus.Command.delete); + } + return result; + } + private String formatCpu(String containerName, Statistics stats) { try { double cpuUsage = 0; diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java index 92971460..3be6850a 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java @@ -115,7 +115,7 @@ public class KubernetesService implements HealthCheck { SharedIndexInformer<Pod> podRunInformer = kubernetesClient().pods().inNamespace(getNamespace()) .withLabels(getRuntimeLabels()).inform(); - podRunInformer.addEventHandlerWithResyncPeriod(new PodEventHandler(infinispanService, this), 30 * 1000L); + podRunInformer.addEventHandlerWithResyncPeriod(new PodEventHandler(infinispanService, this, eventBus), 30 * 1000L); informers.add(podRunInformer); LOGGER.info("Started Kubernetes Informers"); diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java index e60f354b..b46af28c 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java @@ -5,21 +5,28 @@ import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.Quantity; import io.fabric8.kubernetes.api.model.ResourceRequirements; import io.fabric8.kubernetes.client.informers.ResourceEventHandler; +import io.vertx.core.json.JsonObject; +import io.vertx.mutiny.core.eventbus.EventBus; import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.jboss.logging.Logger; +import java.util.List; + import static org.apache.camel.karavan.service.CodeService.DEFAULT_CONTAINER_RESOURCES; +import static org.apache.camel.karavan.shared.EventType.CONTAINER_STATUS; public class PodEventHandler implements ResourceEventHandler<Pod> { private static final Logger LOGGER = Logger.getLogger(PodEventHandler.class.getName()); private final InfinispanService infinispanService; private final KubernetesService kubernetesService; + private final EventBus eventBus; - public PodEventHandler(InfinispanService infinispanService, KubernetesService kubernetesService) { + public PodEventHandler(InfinispanService infinispanService, KubernetesService kubernetesService, EventBus eventBus) { this.infinispanService = infinispanService; this.kubernetesService = kubernetesService; + this.eventBus = eventBus; } @Override @@ -28,7 +35,7 @@ public class PodEventHandler implements ResourceEventHandler<Pod> { LOGGER.info("onAdd " + pod.getMetadata().getName()); ContainerStatus ps = getPodStatus(pod); if (ps != null) { - infinispanService.saveContainerStatus(ps); + eventBus.send(CONTAINER_STATUS, JsonObject.mapFrom(ps)); } } catch (Exception e){ LOGGER.error(e.getMessage(), e.getCause()); @@ -41,7 +48,7 @@ public class PodEventHandler implements ResourceEventHandler<Pod> { LOGGER.info("onUpdate " + newPod.getMetadata().getName()); ContainerStatus ps = getPodStatus(newPod); if (ps != null) { - infinispanService.saveContainerStatus(ps); + eventBus.send(CONTAINER_STATUS, JsonObject.mapFrom(ps)); } } catch (Exception e){ LOGGER.error(e.getMessage(), e.getCause()); @@ -76,17 +83,22 @@ public class PodEventHandler implements ResourceEventHandler<Pod> { String requestCpu = resourceRequirements.getRequests().getOrDefault("cpu", new Quantity()).toString(); String limitMemory = resourceRequirements.getLimits().getOrDefault("memory", new Quantity()).toString(); String limitCpu = resourceRequirements.getLimits().getOrDefault("cpu", new Quantity()).toString(); - return new ContainerStatus( + ContainerStatus status = new ContainerStatus( pod.getMetadata().getName(), - ContainerStatus.Lifecycle.ready, + List.of(ContainerStatus.Command.delete), projectId, kubernetesService.environment, - pod.getMetadata().getName().equals(projectId) ? ContainerStatus.CType.devmode : ContainerStatus.CType.project, + pod.getMetadata().getName().equals(projectId) ? ContainerStatus.ContainerType.devmode : ContainerStatus.ContainerType.project, requestMemory + " : " + limitMemory, requestCpu + " : " + limitCpu, - creationTimestamp + creationTimestamp); - ); + if (ready) { + status.setState(ContainerStatus.State.running.name()); + } else { + status.setState(ContainerStatus.State.created.name()); + } + return status; } catch (Exception ex) { LOGGER.error(ex.getMessage(), ex.getCause()); return null; 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 ba3afb33..884ff168 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 @@ -37,6 +37,8 @@ import java.util.Arrays; import java.util.Objects; import java.util.concurrent.ExecutionException; +import static org.apache.camel.karavan.shared.EventType.CONTAINER_STATUS; + @ApplicationScoped public class CamelService { @@ -71,12 +73,12 @@ public class CamelService { public void reloadProjectCode(String projectId) { LOGGER.info("Reload project code " + projectId); try { - ContainerStatus containerStatus = infinispanService.getDevModeContainerStatus(projectId, environment); infinispanService.getProjectFiles(projectId).forEach(projectFile -> putRequest(projectId, projectFile.getName(), projectFile.getCode(), 1000)); reloadRequest(projectId); + ContainerStatus containerStatus = infinispanService.getDevModeContainerStatus(projectId, environment); containerStatus.setCodeLoaded(true); - infinispanService.saveContainerStatus(containerStatus); + eventBus.send(CONTAINER_STATUS, JsonObject.mapFrom(containerStatus)); } catch (Exception ex) { LOGGER.error(ex.getMessage()); } 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 4d6f0b7a..f3e011f4 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 @@ -9,11 +9,13 @@ import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.camel.karavan.kubernetes.KubernetesService; import org.apache.camel.karavan.shared.ConfigService; import org.apache.camel.karavan.shared.EventType; +import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import java.io.IOException; +import java.util.Objects; import static org.apache.camel.karavan.shared.EventType.*; @@ -22,6 +24,9 @@ public class EventService { private static final Logger LOGGER = Logger.getLogger(EventService.class.getName()); + @ConfigProperty(name = "karavan.environment") + String environment; + @Inject InfinispanService infinispanService; @@ -37,9 +42,6 @@ public class EventService { @Inject ProjectService projectService; - @Inject - ConfigService configService; - @Inject EventBus bus; @@ -73,29 +75,30 @@ public class EventService { } @ConsumeEvent(value = DEVMODE_CONTAINER_READY, blocking = true, ordered = true) - void receiveCommand(JsonObject message) { - LOGGER.info("received Status " + message); - ContainerStatus status = message.mapTo(ContainerStatus.class); - if (!status.getCodeLoaded() && status.getContainerId() != null && status.getLifeCycle().equals(ContainerStatus.Lifecycle.ready)) { + void receiveCommand(String projectId) { + LOGGER.info("DEVMODE_CONTAINER_READY " + projectId); + ContainerStatus status = infinispanService.getContainerStatus(projectId, environment, projectId); + if (!status.getCodeLoaded() && status.getContainerId() != null && status.getState().equals(ContainerStatus.State.running.name())) { if (ConfigService.inKubernetes()) { - camelService.reloadProjectCode(status.getProjectId()); + camelService.reloadProjectCode(projectId); } else { - infinispanService.sendCodeReloadCommand(status.getProjectId()); + infinispanService.sendCodeReloadCommand(projectId); } } } - @ConsumeEvent(value = CONTAINER_STATISTICS, blocking = true, ordered = true) - public void saveStats(JsonObject data) { - String projectId = data.getString("projectId"); - String memory = data.getString("memory"); - String cpu = data.getString("cpu"); + @ConsumeEvent(value = CONTAINER_STATUS, blocking = true, ordered = true) + public void saveContainerStatus(JsonObject data) { if (infinispanService.isReady()) { - ContainerStatus containerStatus = infinispanService.getDevModeContainerStatus(projectId, configService.getConfiguration().getEnvironment()); - if (containerStatus != null) { - containerStatus.setCpuInfo(cpu); - containerStatus.setMemoryInfo(memory); - infinispanService.saveContainerStatus(containerStatus); + ContainerStatus newStatus = data.mapTo(ContainerStatus.class); + System.out.println(newStatus); + ContainerStatus oldStatus = infinispanService.getContainerStatus(newStatus.getProjectId(), newStatus.getEnv(), newStatus.getContainerName()); + if (oldStatus == null || Objects.equals(oldStatus.getInTransit(), Boolean.FALSE)) { + infinispanService.saveContainerStatus(newStatus); + } else if (Objects.equals(oldStatus.getInTransit(), Boolean.TRUE)){ + if (!Objects.equals(oldStatus.getState(), newStatus.getState())) { + infinispanService.saveContainerStatus(newStatus); + } } } } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java index 7886a86d..16fcbeb6 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java @@ -17,19 +17,29 @@ package org.apache.camel.karavan.service; import io.quarkus.scheduler.Scheduled; +import io.vertx.core.eventbus.EventBus; +import io.vertx.core.json.JsonObject; import org.apache.camel.karavan.docker.DockerService; import org.apache.camel.karavan.infinispan.InfinispanService; +import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.camel.karavan.shared.ConfigService; +import org.apache.camel.karavan.shared.EventType; +import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; +import java.util.List; +import java.util.stream.Collectors; @ApplicationScoped public class ScheduledService { private static final Logger LOGGER = Logger.getLogger(ScheduledService.class.getName()); + @ConfigProperty(name = "karavan.environment") + String environment; + @Inject DockerService dockerService; @@ -42,9 +52,25 @@ public class ScheduledService { @Inject InfinispanService infinispanService; - @Scheduled(every = "{karavan.container.statistics.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) - void collectContainersStats() { - dockerService.collectContainersStats(); + @Inject + EventBus eventBus; + + @Scheduled(every = "{karavan.container.status.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) + void collectContainersStatuses() { + if (infinispanService.isReady()) { + List<ContainerStatus> statusesInDocker = dockerService.collectContainersStatuses(); + List<String> namesInDocker = statusesInDocker.stream().map(ContainerStatus::getContainerName).collect(Collectors.toList()); + List<ContainerStatus> statusesInInfinispan = infinispanService.getContainerStatuses(environment); + // clean deleted + statusesInInfinispan.stream().filter(cs -> !namesInDocker.contains(cs.getContainerName())).forEach(containerStatus -> { + infinispanService.deleteContainerStatus(containerStatus); + infinispanService.deleteCamelStatuses(containerStatus.getProjectId(), containerStatus.getEnv()); + }); + // send statuses to save + statusesInDocker.forEach(containerStatus -> { + eventBus.send(EventType.CONTAINER_STATUS, JsonObject.mapFrom(containerStatus)); + }); + } } @Scheduled(every = "{karavan.container.infinispan.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) 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 ac89a948..a1085fb9 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 @@ -10,7 +10,7 @@ public class EventType { public static final String INFINISPAN_STARTED = "INFINISPAN_STARTED"; - public static final String CONTAINER_STATISTICS = "CONTAINER_STATISTICS"; + public static final String CONTAINER_STATUS = "CONTAINER_STATUS"; public static final String DEVMODE_CONTAINER_READY = "DEVMODE_STATUS"; } diff --git a/karavan-web/karavan-app/src/main/resources/application.properties b/karavan-web/karavan-app/src/main/resources/application.properties index 37a78899..93f3db4b 100644 --- a/karavan-web/karavan-app/src/main/resources/application.properties +++ b/karavan-web/karavan-app/src/main/resources/application.properties @@ -4,7 +4,7 @@ karavan.environments=dev karavan.default-runtime=quarkus karavan.runtimes=quarkus,spring-boot karavan.camel.status.interval=off -karavan.container.statistics.interval=3s +karavan.container.status.interval=3s karavan.container.infinispan.interval=5s karavan.devmode.image=ghcr.io/apache/camel-karavan-runner:3.21.1-snapshot karavan.headless.image=entropy1/karavan-headless:3.21.1-SNAPSHOT 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 7a964268..fc0ed2e8 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 @@ -304,7 +304,7 @@ export class KaravanApi { } static async getDevModePodStatus(projectId: string, after: (res: AxiosResponse<ContainerStatus>) => void) { - instance.get('/api/devmode/pod/' + projectId) + instance.get('/api/devmode/container/' + projectId) .then(res => { after(res); }).catch(err => { @@ -312,7 +312,7 @@ export class KaravanApi { }); } - static async reloadDevMode(projectId: string, after: (res: AxiosResponse<any>) => void) { + static async reloadDevModeCode(projectId: string, after: (res: AxiosResponse<any>) => void) { instance.get('/api/devmode/reload/' + projectId) .then(res => { after(res); @@ -330,7 +330,7 @@ export class KaravanApi { }); } - static async runProject(project: Project, verbose: boolean, after: (res: AxiosResponse<string>) => void) { + static async startDevModeContainer(project: Project, verbose: boolean, after: (res: AxiosResponse<string>) => void) { instance.post('/api/devmode' + (verbose ? '/--verbose' : ''), project) .then(res => { after(res); @@ -339,7 +339,7 @@ export class KaravanApi { }); } - static async deleteRunner(name: string, deletePVC: boolean, after: (res: AxiosResponse<any>) => void) { + static async deleteDevModeContainer(name: string, deletePVC: boolean, after: (res: AxiosResponse<any>) => void) { instance.delete('/api/devmode/' + name + "/" + deletePVC) .then(res => { after(res); @@ -453,7 +453,7 @@ export class KaravanApi { } static async getProjectPodStatuses(project: string, env: string, after: (statuses: ContainerStatus[]) => void) { - instance.get('/api/infrastructure/pod/' + project + "/" + env) + instance.get('/api/infrastructure/container/' + project + "/" + env) .then(res => { if (res.status === 200) { after(res.data); @@ -463,8 +463,17 @@ export class KaravanApi { }); } - static async deletePod(environment: string, name: string, after: (res: AxiosResponse<any>) => void) { - instance.delete('/api/infrastructure/pod/' + environment + '/' + name) + static async manageContainer(environment: string, name: string, command: 'run' | 'pause' | 'stop', after: (res: AxiosResponse<any>) => void) { + instance.post('/api/infrastructure/container/' + environment + '/' + name, {command: command}) + .then(res => { + after(res); + }).catch(err => { + after(err); + }); + } + + static async deleteContainer(environment: string, name: string, after: (res: AxiosResponse<any>) => void) { + instance.delete('/api/infrastructure/container/' + environment + '/' + name) .then(res => { after(res); }).catch(err => { diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts index fe0fb19e..3a4ced02 100644 --- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts +++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts @@ -71,6 +71,7 @@ export class ContainerStatus { containerName: string = ''; containerId: string = ''; lifeCycle: string = ''; + state: string = ''; deployment: string = ''; projectId: string = ''; env: string = ''; @@ -80,6 +81,8 @@ export class ContainerStatus { created: string = ''; image: string = ''; ports: [] = []; + commands: string [] = []; + inTransit: boolean = false; public constructor(init?: Partial<ContainerStatus>) { Object.assign(this, init); 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 4fbd9da7..28f6cf2b 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 @@ -14,43 +14,68 @@ import {ProjectEventBus} from "./ProjectEventBus"; export class ProjectService { - public static startRunner(project: Project, verbose: boolean) { - useDevModeStore.setState({status: "starting"}) - KaravanApi.runProject(project, verbose, res => { + public static startDevModeContainer(project: Project, verbose: boolean) { + useDevModeStore.setState({status: "wip"}) + KaravanApi.startDevModeContainer(project, verbose, res => { if (res.status === 200 || res.status === 201) { ProjectEventBus.sendLog("set", ''); useLogStore.setState({showLog: true, type: 'container', podName: res.data}) + useDevModeStore.setState({status: "none"}) } else { // Todo notification } }); } - public static reloadRunner(project: Project) { - useDevModeStore.setState({status: "reloading"}) - KaravanApi.reloadDevMode(project.projectId, res => { + public static reloadDevModeCode(project: Project) { + useDevModeStore.setState({status: "wip"}) + KaravanApi.reloadDevModeCode(project.projectId, res => { if (res.status === 200 || res.status === 201) { // setIsReloadingPod(false); } else { // Todo notification // setIsReloadingPod(false); } + useDevModeStore.setState({status: "none"}) }); } - public static deleteRunner(project: Project) { - useDevModeStore.setState({status: "deleting"}) + public static stopDevModeContainer(project: Project) { + useDevModeStore.setState({status: "wip"}) + KaravanApi.manageContainer("dev", project.projectId, 'stop', res => { + if (res.status === 200) { + useLogStore.setState({showLog: false, type: 'container', isRunning: false}) + } else { + ProjectEventBus.sendAlert(new ToastMessage("Error stopping DevMode container", res.statusText, 'warning')) + } + }); + } + + public static pauseDevModeContainer(project: Project) { + useDevModeStore.setState({status: "wip"}) + KaravanApi.manageContainer("dev", project.projectId, 'pause', res => { + if (res.status === 200) { + useLogStore.setState({showLog: false, type: 'container', isRunning: false}) + } else { + ProjectEventBus.sendAlert(new ToastMessage("Error stopping DevMode container", res.statusText, 'warning')) + } + }); + } + + public static deleteDevModeContainer(project: Project) { + useDevModeStore.setState({status: "wip"}) ProjectEventBus.sendLog("set", ''); - KaravanApi.deleteRunner(project.projectId, false, res => { + KaravanApi.deleteDevModeContainer(project.projectId, false, res => { if (res.status === 202) { useLogStore.setState({showLog: false, type: 'container', isRunning: false}) } else { ProjectEventBus.sendAlert(new ToastMessage("Error delete runner", res.statusText, 'warning')) } + useDevModeStore.setState({status: "none"}) }); } - public static getDevModePodStatus(project: Project) { + public static getDevModeStatus(project: Project) { const projectId = project.projectId; KaravanApi.getDevModePodStatus(projectId, res => { if (res.status === 200) { @@ -59,8 +84,8 @@ export class ProjectService { if (useDevModeStore.getState().podName !== podStatus.containerName){ useDevModeStore.setState({podName: podStatus.containerName}) } - if (useDevModeStore.getState().status !== "running"){ - useDevModeStore.setState({status: "running"}) + if (useDevModeStore.getState().status !== "wip"){ + useDevModeStore.setState({status: "wip"}) useLogStore.setState({isRunning: true}) } useProjectStore.setState({containerStatus: res.data}); diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts index 418d1848..80c99593 100644 --- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts +++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts @@ -137,14 +137,14 @@ export const useFileStore = create<FileState>((set) => ({ interface DevModeState { podName?: string, - status: "none" | "starting" | "deleting"| "reloading" | "running", - setStatus: (status: "none" | "starting" | "deleting"| "reloading" | "running") => void, + status: "none" | "wip", + setStatus: (status: "none" | "wip") => void, } export const useDevModeStore = create<DevModeState>((set) => ({ podName: undefined, status: "none", - setStatus: (status: "none" | "starting" | "deleting"| "reloading" | "running") => { + setStatus: (status: "none" | "wip") => { set((state: DevModeState) => ({ status: status, })); diff --git a/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx index 29241c44..660c2f0d 100644 --- a/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx @@ -2,13 +2,16 @@ import React, {useState} from 'react'; import { Button, Tooltip, - Flex, FlexItem, Label, Badge + Flex, FlexItem, Label, Badge, Spinner } from '@patternfly/react-core'; import '../designer/karavan.css'; import {ExpandableRowContent, Tbody, Td, Tr} from "@patternfly/react-table"; import StopIcon from "@patternfly/react-icons/dist/js/icons/stop-icon"; import PlayIcon from "@patternfly/react-icons/dist/esm/icons/play-icon"; import {ContainerStatus} from "../api/ProjectModels"; +import PauseIcon from "@patternfly/react-icons/dist/esm/icons/pause-icon"; +import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon"; +import {KaravanApi} from "../api/KaravanApi"; interface Props { index: number @@ -18,14 +21,13 @@ interface Props { export const ContainerTableRow = (props: Props) => { const [isExpanded, setIsExpanded] = useState<boolean>(false); - const [running, setRunning] = useState<boolean>(false); const container = props.container; - const env = container.env; + const commands = container.commands; const ports = container.ports; - const icon = running ? <StopIcon/> : <PlayIcon/>; - const tooltip = running ? "Stop container" : "Start container"; - const color = container.lifeCycle === 'ready' ? "green" : "grey"; + const isRunning = container.state === 'running'; + const inTransit = container.inTransit; + const color = container.state === 'running' ? "green" : "grey"; return ( <Tbody isExpanded={isExpanded}> <Tr key={container.containerName}> @@ -48,23 +50,52 @@ export const ContainerTableRow = (props: Props) => { </Td> <Td>{container.image}</Td> <Td> - <Label color={color}>{container.cpuInfo}</Label> + {isRunning && container.cpuInfo && <Label color={color}>{container.cpuInfo}</Label>} </Td> <Td> - <Label color={color}>{container.memoryInfo}</Label> + {isRunning && container.memoryInfo && <Label color={color}>{container.memoryInfo}</Label>} + </Td> + <Td> + {!inTransit && <Label color={color}>{container.state}</Label>} + {inTransit && <Spinner isSVG size="md" aria-label="spinner"/>} </Td> - {/*<Td>{container.environment}</Td>*/} <Td className="project-action-buttons"> - <Flex direction={{default: "row"}} justifyContent={{default: "justifyContentFlexEnd"}} - spaceItems={{default: 'spaceItemsNone'}}> - <FlexItem> - <Tooltip content={tooltip} position={"bottom"}> - <Button variant={"plain"} icon={icon} onClick={e => { - // setProject(project, "delete"); - }}></Button> - </Tooltip> - </FlexItem> - </Flex> + {container.type !== 'internal' && + <Flex direction={{default: "row"}} justifyContent={{default: "justifyContentFlexEnd"}} + spaceItems={{default: 'spaceItemsNone'}}> + <FlexItem> + <Tooltip content={"Start container"} position={"bottom"}> + <Button variant={"plain"} icon={<PlayIcon/>} isDisabled={!commands.includes('run') || inTransit} + onClick={e => { + KaravanApi.manageContainer(container.env, container.containerName, 'run', res => {}); + }}></Button> + </Tooltip> + </FlexItem> + <FlexItem> + <Tooltip content={"Pause container"} position={"bottom"}> + <Button variant={"plain"} icon={<PauseIcon/>} isDisabled={!commands.includes('pause') || inTransit} + onClick={e => { + KaravanApi.manageContainer(container.env, container.containerName, 'pause', res => {}); + }}></Button> + </Tooltip> + </FlexItem> + <FlexItem> + <Tooltip content={"Stop container"} position={"bottom"}> + <Button variant={"plain"} icon={<StopIcon/>} isDisabled={!commands.includes('stop') || inTransit} + onClick={e => { + KaravanApi.manageContainer(container.env, container.containerName, 'stop', res => {}); + }}></Button> + </Tooltip> + </FlexItem> + <FlexItem> + <Tooltip content={"Delete container"} position={"bottom"}> + <Button variant={"plain"} icon={<DeleteIcon/>} isDisabled={!commands.includes('delete') || inTransit} + onClick={e => { + KaravanApi.deleteContainer(container.env, container.containerName, res => {}); + }}></Button> + </Tooltip> + </FlexItem> + </Flex>} </Td> </Tr> {<Tr isExpanded={isExpanded}> @@ -84,7 +115,7 @@ export const ContainerTableRow = (props: Props) => { <Td colSpan={2}> <ExpandableRowContent> <Flex direction={{default: "row"}} cellPadding={"0px"}> - {ports.map(port => <FlexItem>{port}</FlexItem>)} + {ports.map((port, index) => <FlexItem key={index}>{port}</FlexItem>)} </Flex> </ExpandableRowContent> </Td> diff --git a/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx b/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx index 34d44ff7..9d1f6607 100644 --- a/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx @@ -40,7 +40,7 @@ export const ContainersPage = () => { useEffect(() => { const interval = setInterval(() => { onGetProjects() - }, 2000); + }, 700); return () => { clearInterval(interval) }; @@ -111,21 +111,23 @@ export const ContainersPage = () => { function getEmptyState() { return ( - <Tr> - <Td colSpan={8}> - <Bullseye> - {loading && <Spinner className="progress-stepper" isSVG diameter="80px" aria-label="Loading..."/>} - {!loading && - <EmptyState variant={EmptyStateVariant.small}> - <EmptyStateIcon icon={SearchIcon}/> - <Title headingLevel="h2" size="lg"> - No results found - </Title> - </EmptyState> - } - </Bullseye> - </Td> - </Tr> + <Tbody> + <Tr> + <Td colSpan={8}> + <Bullseye> + {loading && <Spinner className="progress-stepper" isSVG diameter="80px" aria-label="Loading..."/>} + {!loading && + <EmptyState variant={EmptyStateVariant.small}> + <EmptyStateIcon icon={SearchIcon}/> + <Title headingLevel="h2" size="lg"> + No results found + </Title> + </EmptyState> + } + </Bullseye> + </Td> + </Tr> + </Tbody> ) } @@ -145,6 +147,7 @@ export const ContainersPage = () => { <Th key='image'>Image</Th> <Th key='cpuInfo'>CPU</Th> <Th key='memoryInfo'>Memory</Th> + <Th key='state'>State</Th> <Th key='action'></Th> </Tr> </Thead> diff --git a/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx index 4f202b92..510372e5 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx @@ -3,12 +3,13 @@ import {Button, Flex, FlexItem, Label, Switch, Tooltip, TooltipPosition} from '@ import '../designer/karavan.css'; import RocketIcon from "@patternfly/react-icons/dist/esm/icons/rocket-icon"; import ReloadIcon from "@patternfly/react-icons/dist/esm/icons/bolt-icon"; -import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/times-circle-icon"; +import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/trash-icon"; import {useDevModeStore, useLogStore, useProjectStore} from "../api/ProjectStore"; import {ProjectService} from "../api/ProjectService"; import {shallow} from "zustand/shallow"; import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon"; import DownIcon from "@patternfly/react-icons/dist/esm/icons/error-circle-o-icon"; +import StopIcon from "@patternfly/react-icons/dist/js/icons/stop-icon"; interface Props { @@ -18,29 +19,18 @@ interface Props { export const DevModeToolbar = (props: Props) => { const [status] = useDevModeStore((state) => [state.status], shallow) - const [project,containerStatus ] = useProjectStore((state) => [state.project, state.containerStatus], shallow) + const [project, containerStatus ] = useProjectStore((state) => [state.project, state.containerStatus], shallow) const [verbose, setVerbose] = useState(false); - - function getColor() { - return getRunning() ? "green" : "grey"; - } - - function getRunning(): boolean { - return containerStatus.lifeCycle === 'ready'; - } - - function getIcon() { - return (getRunning() ? <UpIcon/> : <DownIcon/>) - } - - const isRunning = status === "running"; - const isStartingPod = status === "starting"; - const isReloadingPod = status === "reloading"; - const isDeletingPod = status === "deleting"; + const commands = containerStatus.commands; + const ports = containerStatus.ports; + const isRunning = containerStatus.state === 'running'; + const inTransit = containerStatus.inTransit; + const color = containerStatus.state === 'running' ? "green" : "grey"; + const icon = isRunning ? <UpIcon/> : <DownIcon/>; return (<Flex className="toolbar" direction={{default: "row"}} alignItems={{default: "alignItemsCenter"}}> - {isRunning && <FlexItem> - <Label icon={getIcon()} color={getColor()}> + {<FlexItem> + <Label icon={icon} color={color}> <Tooltip content={"Show log"} position={TooltipPosition.bottom}> <Button variant="link" onClick={e => @@ -50,19 +40,7 @@ export const DevModeToolbar = (props: Props) => { </Tooltip> </Label> </FlexItem>} - {(isRunning || isDeletingPod) && !isReloadingPod && props.reloadOnly !== true && <FlexItem> - <Tooltip content="Stop devmode" position={TooltipPosition.bottom}> - <Button isLoading={isDeletingPod ? true : undefined} - isSmall - variant={"secondary"} - className="project-button" - icon={!isRunning ? <DeleteIcon/> : <div></div>} - onClick={() => ProjectService.deleteRunner(project)}> - {isDeletingPod ? "..." : "Stop"} - </Button> - </Tooltip> - </FlexItem>} - {!isRunning && !isReloadingPod && !isDeletingPod && props.reloadOnly !== true && <FlexItem> + <FlexItem> <Tooltip content="Verbose" position={TooltipPosition.bottom}> <Switch aria-label="verbose" id="verbose" @@ -70,28 +48,50 @@ export const DevModeToolbar = (props: Props) => { onChange={checked => setVerbose(checked)} /> </Tooltip> - </FlexItem>} - {!isRunning && !isReloadingPod && props.reloadOnly !== true && <FlexItem> + </FlexItem> + {!isRunning && <FlexItem> <Tooltip content="Run in developer mode" position={TooltipPosition.bottom}> - <Button isLoading={isStartingPod ? true : undefined} + <Button isLoading={status === 'wip'} isSmall + isDisabled={(!(commands.length === 0) && !commands.includes('run')) || inTransit} variant={"primary"} - className="project-button" - icon={!isStartingPod ? <RocketIcon/> : <div></div>} - onClick={() => ProjectService.startRunner(project, verbose)}> - {isStartingPod ? "..." : "Run"} + icon={<RocketIcon/>} + onClick={() => ProjectService.startDevModeContainer(project, verbose)}> + {"Run"} </Button> </Tooltip> </FlexItem>} - {(isRunning || isReloadingPod) && <FlexItem> + {isRunning && <FlexItem> <Tooltip content="Reload" position={TooltipPosition.bottom}> - <Button isLoading={isReloadingPod ? true : undefined} + <Button isLoading={status === 'wip'} isSmall + isDisabled={inTransit} variant={"primary"} className="project-button" - icon={!isReloadingPod ? <ReloadIcon/> : <div></div>} - onClick={() => ProjectService.reloadRunner(project)}> - {isReloadingPod ? "..." : "Reload"} + icon={<ReloadIcon/>} + onClick={() => ProjectService.reloadDevModeCode(project)}>Reload + </Button> + </Tooltip> + </FlexItem>} + {<FlexItem> + <Tooltip content="Stop container" position={TooltipPosition.bottom}> + <Button isLoading={status === 'wip'} + isSmall + isDisabled={!commands.includes('stop') || inTransit} + variant={"control"} + icon={<StopIcon/>} + onClick={() => ProjectService.stopDevModeContainer(project)}> + </Button> + </Tooltip> + </FlexItem>} + {<FlexItem> + <Tooltip content="Delete container" position={TooltipPosition.bottom}> + <Button isLoading={status === 'wip'} + isSmall + isDisabled={!commands.includes('delete') || inTransit} + variant={"control"} + icon={<DeleteIcon/>} + onClick={() => ProjectService.deleteDevModeContainer(project)}> </Button> </Tooltip> </FlexItem>} diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx index 90b08ed3..cab863b6 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx @@ -26,7 +26,7 @@ export const ProjectPage = () => { useEffect(() => { // TODO: make status request only when started or just opened const interval = setInterval(() => { - ProjectService.getDevModePodStatus(project); + ProjectService.getDevModeStatus(project); }, 1000); return () => { clearInterval(interval) diff --git a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx index 03bc5f9e..29c7247d 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx @@ -35,7 +35,7 @@ export const InfoContainer = (props: Props) => { } function getRunning(): boolean { - return props.containerStatus.lifeCycle === 'ready'; + return props.containerStatus.state === 'running'; } const containerStatus = props.containerStatus; @@ -50,7 +50,7 @@ export const InfoContainer = (props: Props) => { <DescriptionListGroup> <DescriptionListTerm>Status</DescriptionListTerm> <DescriptionListDescription> - {getPodInfoLabel(containerStatus.lifeCycle)} + {getPodInfoLabel(containerStatus.state)} </DescriptionListDescription> </DescriptionListGroup> <DescriptionListGroup> diff --git a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx b/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx index 491a2b80..f32d070e 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx @@ -87,7 +87,7 @@ export class ProjectStatus extends React.Component<Props, State> { }); break; case "pod": - KaravanApi.deletePod(environment, name, (res: any) => { + KaravanApi.deleteContainer(environment, name, (res: any) => { // if (Array.isArray(res) && Array.from(res).length > 0) // this.onRefresh(); }); diff --git a/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx b/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx index 621f58a5..f6affafe 100644 --- a/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx @@ -47,7 +47,7 @@ export const ServicesTableRow = (props: Props) => { <Td>{service.image}</Td> <Td> <Flex direction={{default: "row"}}> - {service.ports.map(port => <FlexItem>{port}</FlexItem>)} + {service.ports.map(port => <FlexItem key={port}>{port}</FlexItem>)} </Flex> </Td> {/*<Td>{service.environment}</Td>*/} @@ -70,7 +70,7 @@ export const ServicesTableRow = (props: Props) => { <Td colSpan={2}> <ExpandableRowContent> <Flex direction={{default: "column"}} cellPadding={"0px"}> - {keys.map(key => <FlexItem>{key + ": " + env[key]}</FlexItem>)} + {keys.map(key => <FlexItem key={key}>{key + ": " + env[key]}</FlexItem>)} </Flex> </ExpandableRowContent> </Td> diff --git a/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/CamelService.java b/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/CamelService.java index 41aa5838..089ade28 100644 --- a/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/CamelService.java +++ b/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/CamelService.java @@ -66,10 +66,10 @@ public class CamelService { public void reloadProjectCode(String projectId) { LOGGER.info("Reload project code " + projectId); try { - ContainerStatus containerStatus = infinispanService.getDevModeContainerStatus(projectId, environment); infinispanService.getProjectFiles(projectId).forEach(projectFile -> putRequest(projectId, projectFile.getName(), projectFile.getCode(), 1000)); reloadRequest(projectId); + ContainerStatus containerStatus = infinispanService.getDevModeContainerStatus(projectId, environment); containerStatus.setCodeLoaded(true); infinispanService.saveContainerStatus(containerStatus); } catch (Exception ex) { @@ -106,7 +106,7 @@ public class CamelService { public void collectCamelStatuses() { if (infinispanService.isReady()) { infinispanService.getContainerStatuses(environment).stream() - .filter(status -> status.getType().equals(ContainerStatus.CType.devmode) || status.getType().equals(ContainerStatus.CType.project)) + .filter(status -> status.getType().equals(ContainerStatus.ContainerType.devmode) || status.getType().equals(ContainerStatus.ContainerType.project)) .forEach(status -> { CamelStatusRequest csr = new CamelStatusRequest(status.getProjectId(), status.getContainerName()); eventBus.publish(CMD_COLLECT_CAMEL_STATUS, JsonObject.mapFrom(csr)); diff --git a/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/HeadlessService.java b/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/HeadlessService.java index e6f8624f..ba2b805b 100644 --- a/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/HeadlessService.java +++ b/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/HeadlessService.java @@ -39,12 +39,11 @@ public class HeadlessService { void onStart(@Observes StartupEvent ev) { LOGGER.info("Starting Headless Karavan"); infinispanService.start(true); - infinispanService.clearAllStatuses(); } @Scheduled(every = "{karavan.camel.status.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) void collectCamelStatuses() { - LOGGER.info("Collect info statuses"); + LOGGER.info("Collect Camel statuses"); // collect Camel statuses camelService.collectCamelStatuses(); } 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 efd1ee2c..ac905f64 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 @@ -67,6 +67,7 @@ public class InfinispanService { private RemoteCache<GroupedKey, PipelineStatus> pipelineStatuses; private RemoteCache<GroupedKey, DeploymentStatus> deploymentStatuses; private RemoteCache<GroupedKey, ContainerStatus> containerStatuses; + private RemoteCache<GroupedKey, Boolean> transits; private RemoteCache<GroupedKey, ServiceStatus> serviceStatuses; private RemoteCache<GroupedKey, CamelStatus> camelStatuses; private RemoteCache<String, String> commits; @@ -107,6 +108,7 @@ public class InfinispanService { serviceStatuses = getOrCreateCache(ServiceStatus.CACHE, false); camelStatuses = getOrCreateCache(CamelStatus.CACHE, false); commits = getOrCreateCache("commits", false); + transits = getOrCreateCache("transits", false); deploymentStatuses = getOrCreateCache(DeploymentStatus.CACHE, false); codeReloadCommands = getOrCreateCache("code_reload_commands", true); @@ -250,6 +252,18 @@ public class InfinispanService { return new ArrayList<>(serviceStatuses.values()); } + public List<Boolean> getTransits() { + return new ArrayList<>(transits.values()); + } + + public Boolean getTransit(String projectId, String env, String containerName) { + return transits.get(GroupedKey.create(projectId, env, containerName)); + } + + public void setTransit(String projectId, String env, String containerName) { + transits.put(GroupedKey.create(projectId,env,containerName), true); + } + public List<ContainerStatus> getContainerStatuses() { return new ArrayList<>(containerStatuses.values()); } @@ -262,6 +276,12 @@ public class InfinispanService { .execute().list(); } + public void setContainerStatusTransit(String projectId, String env, String containerName) { + ContainerStatus cs = getContainerStatus(projectId, env, containerName); + cs.setInTransit(true); + saveContainerStatus(cs); + } + public ContainerStatus getContainerStatus(String projectId, String env, String containerName) { return containerStatuses.get(GroupedKey.create(projectId, env, containerName)); } @@ -353,14 +373,14 @@ public class InfinispanService { public List<ContainerStatus> getLoadedDevModeStatuses() { QueryFactory queryFactory = Search.getQueryFactory(containerStatuses); return queryFactory.<ContainerStatus>create("FROM karavan.ContainerStatus WHERE type = :type AND codeLoaded = true") - .setParameter("type", ContainerStatus.CType.devmode) + .setParameter("type", ContainerStatus.ContainerType.devmode) .execute().list(); } public List<ContainerStatus> getDevModeStatuses() { QueryFactory queryFactory = Search.getQueryFactory(containerStatuses); return queryFactory.<ContainerStatus>create("FROM karavan.ContainerStatus WHERE type = :type") - .setParameter("type", ContainerStatus.CType.devmode) + .setParameter("type", ContainerStatus.ContainerType.devmode) .execute().list(); } diff --git a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java index de1fea4e..ef7ca779 100644 --- a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java +++ b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java @@ -9,7 +9,16 @@ import java.util.List; public class ContainerStatus { - public enum CType { + public enum State { + created, + running, + restarting, + paused, + exited, + dead + } + + public enum ContainerType { @ProtoEnumValue(number = 0, name = "internal") internal, @ProtoEnumValue(number = 1, name = "devmode") devmode, @ProtoEnumValue(number = 2, name = "devservice") devservice, @@ -17,10 +26,11 @@ public class ContainerStatus { @ProtoEnumValue(number = 5, name = "unknown") unknown, } - public enum Lifecycle { - @ProtoEnumValue(number = 0, name = "init") init, - @ProtoEnumValue(number = 1, name = "ready") ready, - @ProtoEnumValue(number = 2, name = "deleting") deleting, + public enum Command { + @ProtoEnumValue(number = 0, name = "run") run, + @ProtoEnumValue(number = 1, name = "pause") pause, + @ProtoEnumValue(number = 2, name = "stop") stop, + @ProtoEnumValue(number = 3, name = "delete") delete, } public static final String CACHE = "container_statuses"; @@ -37,7 +47,7 @@ public class ContainerStatus { @ProtoField(number = 6) String env; @ProtoField(number = 7) - CType type; + ContainerType type; @ProtoField(number = 8) String memoryInfo; @ProtoField(number = 9) @@ -45,14 +55,16 @@ public class ContainerStatus { @ProtoField(number = 10) String created; @ProtoField(number = 11) - Lifecycle lifeCycle; + List<Command> commands; @ProtoField(number = 12) - Boolean codeLoaded; + String state; @ProtoField(number = 13) - Boolean logging; + Boolean codeLoaded; + @ProtoField(number = 14) + Boolean inTransit = false; @ProtoFactory - public ContainerStatus(String projectId, String containerName, String containerId, String image, List<Integer> ports, String env, CType type, String memoryInfo, String cpuInfo, String created, Lifecycle lifeCycle, Boolean codeLoaded, Boolean logging) { + public ContainerStatus(String projectId, String containerName, String containerId, String image, List<Integer> ports, String env, ContainerType type, String memoryInfo, String cpuInfo, String created, List<Command> commands, String state, Boolean codeLoaded, Boolean inTransit) { this.projectId = projectId; this.containerName = containerName; this.containerId = containerId; @@ -63,14 +75,15 @@ public class ContainerStatus { this.memoryInfo = memoryInfo; this.cpuInfo = cpuInfo; this.created = created; - this.lifeCycle = lifeCycle; + this.commands = commands; + this.state = state; this.codeLoaded = codeLoaded; - this.logging = logging; + this.inTransit = inTransit; } - public ContainerStatus(String containerName, Lifecycle lifeCycle, String projectId, String env, CType type, String memoryInfo, String cpuInfo, String created) { + public ContainerStatus(String containerName, List<Command> commands, String projectId, String env, ContainerType type, String memoryInfo, String cpuInfo, String created) { this.containerName = containerName; - this.lifeCycle = lifeCycle; + this.commands = commands; this.projectId = projectId; this.env = env; this.type = type; @@ -79,9 +92,9 @@ public class ContainerStatus { this.created = created; } - public ContainerStatus(String containerName, Lifecycle lifeCycle, String projectId, String env, CType type, String created) { + public ContainerStatus(String containerName, List<Command> commands, String projectId, String env, ContainerType type, String created) { this.containerName = containerName; - this.lifeCycle = lifeCycle; + this.commands = commands; this.projectId = projectId; this.env = env; this.created = created; @@ -89,17 +102,18 @@ public class ContainerStatus { } public static ContainerStatus createDevMode(String projectId, String env) { - return new ContainerStatus(projectId, projectId, null, null, null, env, CType.devmode, null, null, null, Lifecycle.init, false, false); + return new ContainerStatus(projectId, projectId, null, null, null, env, ContainerType.devmode, null, null, null, List.of(Command.run), null, false, false); } - public static ContainerStatus createWithId(String name, String env, String containerId, String image, List<Integer> ports, CType type, Lifecycle lifeCycle, String created) { + public static ContainerStatus createWithId(String name, String env, String containerId, String image, List<Integer> ports, ContainerType type, List<Command> commands, String status, String created) { return new ContainerStatus(name, name, containerId, image, ports, env, type, - null, null, created, lifeCycle, false, false); + null, null, created, commands, status, false, false); } public ContainerStatus() { } + public String getProjectId() { return projectId; } @@ -148,11 +162,11 @@ public class ContainerStatus { this.env = env; } - public CType getType() { + public ContainerType getType() { return type; } - public void setType(CType type) { + public void setType(ContainerType type) { this.type = type; } @@ -180,12 +194,20 @@ public class ContainerStatus { this.created = created; } - public Lifecycle getLifeCycle() { - return lifeCycle; + public List<Command> getCommands() { + return commands; } - public void setLifeCycle(Lifecycle lifeCycle) { - this.lifeCycle = lifeCycle; + public void setCommands(List<Command> commands) { + this.commands = commands; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; } public Boolean getCodeLoaded() { @@ -196,11 +218,31 @@ public class ContainerStatus { this.codeLoaded = codeLoaded; } - public Boolean getLogging() { - return logging; - } - - public void setLogging(Boolean logging) { - this.logging = logging; + public Boolean getInTransit() { + return inTransit; + } + + public void setInTransit(Boolean inTransit) { + this.inTransit = inTransit; + } + + @Override + public String toString() { + return "ContainerStatus{" + + "projectId='" + projectId + '\'' + + ", containerName='" + containerName + '\'' + + ", containerId='" + containerId + '\'' + + ", image='" + image + '\'' + + ", ports=" + ports + + ", env='" + env + '\'' + + ", type=" + type + + ", memoryInfo='" + memoryInfo + '\'' + + ", cpuInfo='" + cpuInfo + '\'' + + ", created='" + created + '\'' + + ", commands=" + commands + + ", state='" + state + '\'' + + ", codeLoaded=" + codeLoaded + + ", logging=" + inTransit + + '}'; } } diff --git a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/KaravanSchema.java b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/KaravanSchema.java index c20115b3..29731935 100644 --- a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/KaravanSchema.java +++ b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/KaravanSchema.java @@ -14,8 +14,8 @@ import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder; CamelStatus.Name.class, DeploymentStatus.class, ContainerStatus.class, - ContainerStatus.CType.class, - ContainerStatus.Lifecycle.class, + ContainerStatus.ContainerType.class, + ContainerStatus.Command.class, ServiceStatus.class }, schemaFileName = "karavan.proto",