You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2023/07/23 00:54:30 UTC

[camel-karavan] branch main updated (b8ac47a4 -> a26b5660)

This is an automated email from the ASF dual-hosted git repository.

marat pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git


    from b8ac47a4 karavan-app #817
     new f6a60c39 Working prototype #817
     new a26b5660 Working prototype #817

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .github/workflows/app.yml                          |  17 ++-
 .../workflows/{builder.yml => docker-builder.yml}  |   0
 .../workflows/{runner.yml => docker-devmode.yml}   |   2 +-
 .github/workflows/headless.yml                     |  56 ++++++++
 karavan-web/karavan-app/pom.xml                    |   5 +-
 .../apache/camel/karavan/api/DevModeResource.java  |  59 ++++----
 .../camel/karavan/api/InfrastructureResource.java  |   6 +-
 .../apache/camel/karavan/api/KameletResources.java |  14 +-
 .../camel/karavan/docker/DockerEventListener.java  | 115 ++++++++--------
 .../apache/camel/karavan/docker/DockerService.java |  24 ++--
 .../karavan/kubernetes/KubernetesService.java      |  15 ++-
 .../camel/karavan/kubernetes/PodEventHandler.java  |  25 ++--
 .../apache/camel/karavan/service/CamelService.java |  55 +-------
 .../apache/camel/karavan/service/EventService.java |  52 ++-----
 .../camel/karavan/service/KaravanService.java      |  37 ++++-
 .../camel/karavan/service/ScheduledService.java    |   9 +-
 .../apache/camel/karavan/shared/ConfigService.java |   5 +-
 .../org/apache/camel/karavan/shared/EventType.java |   5 +-
 .../src/main/resources/application.properties      |   2 +-
 .../karavan-app/src/main/webui/src/Main.tsx        |   4 +-
 .../src/main/webui/src/api/KaravanApi.tsx          |   6 +-
 .../src/main/webui/src/api/ProjectModels.ts        |  12 +-
 .../src/main/webui/src/api/ProjectService.ts       |  10 +-
 .../src/main/webui/src/api/ProjectStore.ts         |   6 +-
 .../src/main/webui/src/project/DevModeToolbar.tsx  |  22 ++-
 .../webui/src/project/dashboard/DashboardTab.tsx   |   8 +-
 .../dashboard/{InfoPod.tsx => InfoContainer.tsx}   |  23 ++--
 .../webui/src/project/pipeline/ProjectStatus.tsx   |  18 +--
 .../src/main/webui/src/project/trace/TraceTab.tsx  |   1 +
 .../src/main/docker/Dockerfile.distroless          |   2 +-
 .../camel/karavan/headless/CamelService.java       |  67 +++------
 .../camel/karavan/headless/EventService.java       |  12 +-
 .../camel/karavan/headless/HeadlessService.java    |   2 -
 .../karavan/infinispan/InfinispanService.java      |  70 ++++------
 .../karavan/infinispan/model/ContainerInfo.java    |  70 ----------
 .../karavan/infinispan/model/ContainerStatus.java  | 150 ++++++++++++++++-----
 .../karavan/infinispan/model/DevModeStatus.java    |  78 -----------
 .../karavan/infinispan/model/KaravanSchema.java    |   5 +-
 .../camel/karavan/infinispan/DataGridTest.java     |  11 +-
 karavan-web/pom.xml                                |   2 +-
 40 files changed, 498 insertions(+), 584 deletions(-)
 rename .github/workflows/{builder.yml => docker-builder.yml} (100%)
 rename .github/workflows/{runner.yml => docker-devmode.yml} (97%)
 create mode 100644 .github/workflows/headless.yml
 rename karavan-web/karavan-app/src/main/webui/src/project/dashboard/{InfoPod.tsx => InfoContainer.tsx} (77%)
 delete mode 100644 karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerInfo.java
 delete mode 100644 karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/DevModeStatus.java


[camel-karavan] 01/02: Working prototype #817

Posted by ma...@apache.org.
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 f6a60c39d44671df704676afff13e7806d30c84c
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Sat Jul 22 19:56:16 2023 -0400

    Working prototype #817
---
 karavan-web/karavan-app/pom.xml                    |   5 +-
 .../apache/camel/karavan/api/DevModeResource.java  |  49 ++++---
 .../camel/karavan/api/InfrastructureResource.java  |   6 +-
 .../camel/karavan/docker/DockerEventListener.java  | 114 ++++++++--------
 .../apache/camel/karavan/docker/DockerService.java |  22 +--
 .../karavan/kubernetes/KubernetesService.java      |  11 +-
 .../camel/karavan/kubernetes/PodEventHandler.java  |  25 ++--
 .../apache/camel/karavan/service/CamelService.java |  55 +-------
 .../apache/camel/karavan/service/EventService.java |  21 +--
 .../camel/karavan/service/ScheduledService.java    |   9 +-
 .../apache/camel/karavan/shared/ConfigService.java |   5 +-
 .../org/apache/camel/karavan/shared/EventType.java |   2 +-
 .../src/main/resources/application.properties      |   2 +-
 .../src/main/docker/Dockerfile.distroless          |   2 +-
 .../camel/karavan/headless/CamelService.java       |  67 +++------
 .../camel/karavan/headless/EventService.java       |  12 +-
 .../camel/karavan/headless/HeadlessService.java    |   2 -
 .../karavan/infinispan/InfinispanService.java      |  68 +++-------
 .../karavan/infinispan/model/ContainerInfo.java    |  70 ----------
 .../karavan/infinispan/model/ContainerStatus.java  | 150 ++++++++++++++++-----
 .../karavan/infinispan/model/DevModeStatus.java    |  78 -----------
 .../karavan/infinispan/model/KaravanSchema.java    |   5 +-
 .../camel/karavan/infinispan/DataGridTest.java     |  11 +-
 karavan-web/pom.xml                                |   2 +-
 24 files changed, 315 insertions(+), 478 deletions(-)

diff --git a/karavan-web/karavan-app/pom.xml b/karavan-web/karavan-app/pom.xml
index f38269f9..4cfebe81 100644
--- a/karavan-web/karavan-app/pom.xml
+++ b/karavan-web/karavan-app/pom.xml
@@ -131,8 +131,9 @@
             <artifactId>quarkus-container-image-docker</artifactId>
         </dependency>
         <dependency>
-            <groupId>io.quarkus</groupId>
-            <artifactId>quarkus-container-image-s2i</artifactId>
+            <groupId>io.quarkiverse.quinoa</groupId>
+            <artifactId>quarkus-quinoa</artifactId>
+            <version>${quinoa.version}</version>
         </dependency>
         <dependency>
             <groupId>io.quarkus</groupId>
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 a5372ed9..c3d6184c 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
@@ -20,7 +20,6 @@ import org.apache.camel.karavan.docker.DockerService;
 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.infinispan.model.DevModeStatus;
 import org.apache.camel.karavan.infinispan.model.Project;
 import org.apache.camel.karavan.kubernetes.KubernetesService;
 import org.apache.camel.karavan.service.CamelService;
@@ -33,8 +32,6 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import java.util.Optional;
 
-import static org.apache.camel.karavan.shared.ConfigService.DEVMODE_SUFFIX;
-
 @Path("/api/devmode")
 public class DevModeResource {
 
@@ -58,16 +55,16 @@ public class DevModeResource {
     @Consumes(MediaType.APPLICATION_JSON)
     @Path("/{jBangOptions}")
     public Response runProjectWithJBangOptions(Project project, @PathParam("jBangOptions") String jBangOptions) throws InterruptedException {
-        String runnerName = project.getProjectId() + DEVMODE_SUFFIX;
-        ContainerStatus status = infinispanService.getDevModeContainerStatuses(project.getProjectId(), environment);
+        String containerName = project.getProjectId();
+        ContainerStatus status = infinispanService.getDevModeContainerStatus(project.getProjectId(), environment);
         if (status == null) {
-            infinispanService.saveDevModeStatus(new DevModeStatus(project.getProjectId(), null, null, false));
+            infinispanService.saveContainerStatus(ContainerStatus.createDevMode(project.getProjectId(), environment));
             if (ConfigService.inKubernetes()) {
-                kubernetesService.runDevModeContainer(project, runnerName, "");
+                kubernetesService.runDevModeContainer(project, "");
             } else {
-                dockerService.runDevmodeContainer(project, runnerName, "");
+                dockerService.runDevmodeContainer(project, "");
             }
-            return Response.ok(runnerName).build();
+            return Response.ok(containerName).build();
         }
         return Response.notModified().build();
     }
@@ -83,7 +80,11 @@ public class DevModeResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/reload/{projectId}")
     public Response reload(@PathParam("projectId") String projectId) {
-        camelService.reloadProjectCode(projectId);
+        if (ConfigService.inKubernetes()) {
+            camelService.reloadProjectCode(projectId);
+        } else {
+            infinispanService.sendCodeReloadCommand(projectId);
+        }
         return Response.ok().build();
     }
 
@@ -91,15 +92,18 @@ public class DevModeResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Consumes(MediaType.APPLICATION_JSON)
     @Path("/{projectId}/{deletePVC}")
-    public Response deleteRunner(@PathParam("projectId") String projectId, @PathParam("deletePVC") boolean deletePVC) {
-        String runnerName = projectId + DEVMODE_SUFFIX;
+    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);
+        }
         if (ConfigService.inKubernetes()) {
-            kubernetesService.deleteRunner(runnerName, deletePVC);
+            kubernetesService.deleteRunner(projectId, deletePVC);
         } else {
-            dockerService.stopContainer(runnerName);
-            dockerService.deleteContainer(runnerName);
+            dockerService.stopContainer(projectId);
+            dockerService.deleteContainer(projectId);
         }
-        infinispanService.deleteDevModeStatus(projectId);
         return Response.accepted().build();
     }
 
@@ -107,12 +111,13 @@ public class DevModeResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/pod/{projectId}")
     public Response getPodStatus(@PathParam("projectId") String projectId) throws RuntimeException {
-        String runnerName = projectId + DEVMODE_SUFFIX;
-        Optional<ContainerStatus> ps =  infinispanService.getContainerStatuses(projectId, environment).stream()
-                .filter(podStatus -> podStatus.getName().equals(runnerName))
-                .findFirst();
-        if (ps.isPresent()) {
-            return Response.ok(ps.get()).build();
+        if (infinispanService.isReady()) {
+            ContainerStatus cs = infinispanService.getDevModeContainerStatus(projectId, environment);
+            if (cs != null) {
+                return Response.ok(cs).build();
+            } else {
+                return Response.noContent().build();
+            }
         } else {
             return Response.noContent().build();
         }
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 668c9770..2a7ec6c2 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
@@ -155,8 +155,8 @@ public class InfrastructureResource {
     @Path("/pod/{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.pod))
-                .sorted(Comparator.comparing(ContainerStatus::getName))
+                .filter(podStatus -> Objects.equals(podStatus.getType(), ContainerStatus.CType.project))
+                .sorted(Comparator.comparing(ContainerStatus::getContainerName))
                 .collect(Collectors.toList());
     }
 
@@ -205,7 +205,7 @@ public class InfrastructureResource {
         if (ConfigService.inKubernetes()) {
             return Response.ok(kubernetesService.getServices(kubernetesService.getNamespace())).build();
         } else {
-            List<String> list = infinispanService.getContainerInfos(environment).stream()
+            List<String> list = infinispanService.getContainerStatuses(environment).stream()
                     .map(ci -> ci.getPorts().stream().map(i -> ci.getContainerName() + ":" + i).collect(Collectors.toList()))
                     .flatMap(List::stream).collect(Collectors.toList());
             return Response.ok(list).build();
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 40c2b858..4a9bd265 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
@@ -8,8 +8,6 @@ import com.github.dockerjava.api.model.EventType;
 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.ContainerInfo;
-import org.apache.camel.karavan.infinispan.model.DevModeStatus;
 import org.apache.camel.karavan.infinispan.model.ContainerStatus;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.jboss.logging.Logger;
@@ -18,7 +16,6 @@ import javax.enterprise.context.ApplicationScoped;
 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;
@@ -26,9 +23,8 @@ import java.util.Objects;
 import java.util.stream.Collectors;
 
 import static org.apache.camel.karavan.docker.DockerService.LABEL_TYPE;
-import static org.apache.camel.karavan.shared.EventType.DEVMODE_STATUS;
+import static org.apache.camel.karavan.shared.EventType.DEVMODE_CONTAINER_READY;
 import static org.apache.camel.karavan.shared.EventType.INFINISPAN_STARTED;
-import static org.apache.camel.karavan.shared.ConfigService.DEVMODE_SUFFIX;
 
 @ApplicationScoped
 public class DockerEventListener implements ResultCallback<Event> {
@@ -58,12 +54,6 @@ public class DockerEventListener implements ResultCallback<Event> {
             if (Objects.equals(event.getType(), EventType.CONTAINER)) {
                 Container container = dockerService.getContainer(event.getId());
                 onContainerEvent(event, container);
-                String status = event.getStatus();
-                if (container.getNames()[0].equals("/infinispan") && status.startsWith("health_status:")) {
-                    onInfinispanHealthEvent(event, container);
-                } else if (Objects.equals(container.getLabels().get(LABEL_TYPE), ContainerStatus.CType.devmode.name())) {
-                    onDevModeEvent(event, container);
-                }
             }
         } catch (Exception exception) {
             LOGGER.error(exception.getMessage());
@@ -72,66 +62,72 @@ public class DockerEventListener implements ResultCallback<Event> {
 
     public void onContainerEvent(Event event, Container container) {
         if (infinispanService.isReady()) {
-            String name = container.getNames()[0].replace("/", "");
             if (Arrays.asList("destroy", "stop", "die", "kill", "pause", "destroy", "rename").contains(event.getStatus())) {
-                infinispanService.deleteContainerInfo(name);
+                onDeleteContainer(container);
             } else if (Arrays.asList("create", "start", "unpause").contains(event.getStatus())) {
-                List<Integer> ports = Arrays.stream(container.getPorts()).map(ContainerPort::getPrivatePort).filter(Objects::nonNull).collect(Collectors.toList());
-                ContainerInfo ci = new ContainerInfo(name, container.getId(), container.getImage(), ports, environment);
-                infinispanService.saveContainerInfo(ci);
+                onCreateContainer(container, event);
+            } 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);
+                    }
+                }
             }
         }
     }
 
-    public void onInfinispanHealthEvent(Event event, Container container) {
+    private void onDeleteContainer(Container container){
+        String name = container.getNames()[0].replace("/", "");
+        infinispanService.deleteContainerStatus(name, environment, name);
+        if (inDevMode(container)) {
+            infinispanService.deleteCamelStatuses(name, environment);
+        }
+    }
+
+    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());
+        ContainerStatus ci = infinispanService.getContainerStatus(name, environment, name);
+        if (ci == null) {
+            ci = ContainerStatus.createWithId(name, environment, container.getId(), ports, type, lc);
+        } else {
+            ci.setContainerId(container.getId());
+            ci.setPorts(ports);
+            ci.setType(type);
+            ci.setLifeCycle(lc);
+        }
+        infinispanService.saveContainerStatus(ci);
+    }
+
+    public void onInfinispanHealthEvent(Container container, Event event) {
         String status = event.getStatus();
         String health = status.replace("health_status: ", "");
         LOGGER.infof("Container %s health status: %s", container.getNames()[0], health);
         eventBus.publish(INFINISPAN_STARTED, health);
     }
 
-    public void onDevModeEvent(Event event, Container container) {
-        try {
-            if (infinispanService.isReady()) {
-                String status = event.getStatus();
-                String name = container.getNames()[0].replace("/", "");
-                if (Arrays.asList("stop", "die", "kill", "pause", "destroy").contains(event.getStatus())) {
-                    String projectId = name.replace(DEVMODE_SUFFIX, "");
-                    infinispanService.deleteDevModeStatus(projectId);
-                    infinispanService.deleteContainerStatus(projectId, environment, name);
-                    infinispanService.deleteCamelStatuses(projectId, environment);
-                } else if (Arrays.asList("start", "unpause").contains(event.getStatus())) {
-                    saveContainerStatus(container);
-                } else if (status.startsWith("health_status:")) {
-                    String health = status.replace("health_status: ", "");
-                    LOGGER.infof("Container %s health status: %s", container.getNames()[0], health);
-                    //update DevModeStatus
-                    String containerName = container.getNames()[0].replace("/", "");
-                    DevModeStatus dms = infinispanService.getDevModeStatus(container.getLabels().get("projectId"));
-                    if (dms != null) {
-                        dms.setContainerName(containerName);
-                        dms.setContainerId(container.getId());
-                        infinispanService.saveDevModeStatus(dms);
-                        eventBus.publish(DEVMODE_STATUS, JsonObject.mapFrom(dms));
-                    }
-                }
-            }
-        } catch (Exception exception) {
-            LOGGER.error(exception.getMessage());
+    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));
         }
     }
 
-    protected void saveContainerStatus(Container container){
-        String name = container.getNames()[0].replace("/", "");
-        String projectId = name.replace(DEVMODE_SUFFIX, "");
-        Integer exposedPort =  (container.getPorts().length > 0)  ? container.getPorts()[0].getPublicPort() : null;
-        if (infinispanService.isReady()) {
-            ContainerStatus ps = infinispanService.getDevModeContainerStatuses(projectId, environment);
-            if (ps == null) {
-                ps = new ContainerStatus(name, true, projectId, environment, getCtype(container.getLabels()), Instant.ofEpochSecond(container.getCreated()).toString());
-            }
-            infinispanService.saveContainerStatus(ps);
-        }
+    private boolean inDevMode(Container container) {
+        return Objects.equals(getCtype(container.getLabels()), ContainerStatus.CType.devmode);
     }
 
     private ContainerStatus.CType getCtype(Map<String, String> labels) {
@@ -140,8 +136,12 @@ public class DockerEventListener implements ResultCallback<Event> {
             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;
         }
-        return ContainerStatus.CType.container;
+        return ContainerStatus.CType.unknown;
     }
 
     @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 f2a8bd8e..76ed2c62 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
@@ -28,7 +28,6 @@ import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
-import static org.apache.camel.karavan.shared.ConfigService.DEVMODE_SUFFIX;
 import static org.apache.camel.karavan.shared.EventType.*;
 
 @ApplicationScoped
@@ -40,12 +39,13 @@ public class DockerService {
     public static final String KARAVAN_CONTAINER_NAME = "karavan-headless";
 
     public static final String NETWORK_NAME = "karavan";
-    public static final String LABEL_TYPE = "type";
-    public static final String LABEL_PROJECT_ID = "projectId";
+    public static final String LABEL_TYPE = "org.apache.camel.karavan.type";
+    public static final String LABEL_PROJECT_ID = "org.apache.camel.karavan.projectId";
     private static final DecimalFormat formatCpu = new DecimalFormat("0.00");
     private static final DecimalFormat formatMiB = new DecimalFormat("0.0");
     private static final DecimalFormat formatGiB = new DecimalFormat("0.00");
     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.devmode.image")
     String devmodeImage;
@@ -70,15 +70,15 @@ public class DockerService {
     @Inject
     EventBus eventBus;
 
-    public void runDevmodeContainer(Project project, String runnerName, String jBangOptions) throws InterruptedException {
+    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(runnerName, devmodeImage,
+        createContainer(projectId, devmodeImage,
                 List.of(), null, false, false, healthCheck,
                 Map.of(LABEL_TYPE, ContainerStatus.CType.devmode.name(), LABEL_PROJECT_ID, projectId));
-        startContainer(runnerName);
+        startContainer(projectId);
         LOGGER.infof("DevMode started for %s", projectId);
     }
 
@@ -86,7 +86,7 @@ public class DockerService {
         try {
             LOGGER.info("Infinispan is starting...");
 
-            HealthCheck healthCheck = new HealthCheck().withTest(List.of("CMD", "curl", "-f", "http://localhost:11222/rest/v2/cache-managers/default/health/status"))
+            HealthCheck healthCheck = new HealthCheck().withTest(infinispanHealthCheckCMD)
                     .withInterval(10000000000L).withTimeout(10000000000L).withStartPeriod(10000000000L).withRetries(30);
 
             createContainer(INFINISPAN_CONTAINER_NAME, infinispanImage,
@@ -131,11 +131,10 @@ public class DockerService {
             Statistics stats = getContainerStats(container.getId());
 
             String name = container.getNames()[0].replace("/", "");
-            String projectId = name.replace(DEVMODE_SUFFIX, "");
             String memoryUsage = formatMemory(stats.getMemoryStats().getUsage());
             String memoryLimit = formatMemory(stats.getMemoryStats().getLimit());
             JsonObject data = JsonObject.of(
-                    "projectId", projectId,
+                    "projectId", name,
                     "memory", memoryUsage + " / " + memoryLimit,
                     "cpu", formatCpu(name, stats)
             );
@@ -147,7 +146,10 @@ public class DockerService {
         getDockerClient().listContainersCmd().exec().forEach(container -> {
             String name = container.getNames()[0].replace("/", "");
             if (!Objects.equals(name, INFINISPAN_CONTAINER_NAME)) {
-                dockerEventListener.saveContainerStatus(container);
+                dockerEventListener.onCreateContainer(container, new Event(container.getStatus(), container.getId(), container.getImage(), container.getCreated()));
+            }
+            if (Objects.equals(container.getLabels().get(LABEL_TYPE), ContainerStatus.CType.devmode.name())) {
+                dockerEventListener.onCreateContainer(container, new Event(container.getStatus(), container.getId(), container.getImage(), container.getCreated()));
             }
         });
     }
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 f10a38b6..e1f231b2 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
@@ -385,18 +385,19 @@ public class KubernetesService implements HealthCheck {
         return result;
     }
 
-    public void runDevModeContainer(Project project, String runnerName, String jBangOptions) {
-        createPVC(runnerName);
-        Pod old = kubernetesClient().pods().inNamespace(getNamespace()).withName(runnerName).get();
+    public void runDevModeContainer(Project project, String jBangOptions) {
+        String name = project.getProjectId();
+        createPVC(name);
+        Pod old = kubernetesClient().pods().inNamespace(getNamespace()).withName(name).get();
         if (old == null) {
             ProjectFile properties = infinispanService.getProjectFile(project.getProjectId(), APPLICATION_PROPERTIES_FILENAME);
             Map<String, String> containerResources = CodeService
                     .getRunnerContainerResourcesMap(properties, isOpenshift(), project.getRuntime().equals("quarkus"));
-            Pod pod = getRunnerPod(project.getProjectId(), runnerName, jBangOptions, containerResources);
+            Pod pod = getRunnerPod(project.getProjectId(), name, jBangOptions, containerResources);
             Pod result = kubernetesClient().resource(pod).createOrReplace();
             LOGGER.info("Created pod " + result.getMetadata().getName());
         }
-        createService(runnerName);
+        createService(name);
     }
 
     public void deleteRunner(String name, boolean deletePVC) {
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 791d6bfc..e60f354b 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
@@ -10,7 +10,6 @@ import org.apache.camel.karavan.infinispan.model.ContainerStatus;
 import org.jboss.logging.Logger;
 
 import static org.apache.camel.karavan.service.CodeService.DEFAULT_CONTAINER_RESOURCES;
-import static org.apache.camel.karavan.shared.ConfigService.DEVMODE_SUFFIX;
 
 public class PodEventHandler implements ResourceEventHandler<Pod> {
 
@@ -28,7 +27,9 @@ public class PodEventHandler implements ResourceEventHandler<Pod> {
         try {
             LOGGER.info("onAdd " + pod.getMetadata().getName());
             ContainerStatus ps = getPodStatus(pod);
-            infinispanService.saveContainerStatus(ps);
+            if (ps != null) {
+                infinispanService.saveContainerStatus(ps);
+            }
         } catch (Exception e){
             LOGGER.error(e.getMessage(), e.getCause());
         }
@@ -39,7 +40,9 @@ public class PodEventHandler implements ResourceEventHandler<Pod> {
         try {
             LOGGER.info("onUpdate " + newPod.getMetadata().getName());
             ContainerStatus ps = getPodStatus(newPod);
-            infinispanService.saveContainerStatus(ps);
+            if (ps != null) {
+                infinispanService.saveContainerStatus(ps);
+            }
         } catch (Exception e){
             LOGGER.error(e.getMessage(), e.getCause());
         }
@@ -60,7 +63,7 @@ public class PodEventHandler implements ResourceEventHandler<Pod> {
 
     public ContainerStatus getPodStatus(Pod pod) {
         String deployment = pod.getMetadata().getLabels().get("app");
-        String project = deployment != null ? deployment : pod.getMetadata().getLabels().get("karavan/projectId");
+        String projectId = deployment != null ? deployment : pod.getMetadata().getLabels().get("karavan/projectId");
         try {
             boolean ready = pod.getStatus().getConditions().stream().anyMatch(c -> c.getType().equals("Ready"));
             String creationTimestamp = pod.getMetadata().getCreationTimestamp();
@@ -75,10 +78,10 @@ public class PodEventHandler implements ResourceEventHandler<Pod> {
             String limitCpu = resourceRequirements.getLimits().getOrDefault("cpu", new Quantity()).toString();
             return new ContainerStatus(
                     pod.getMetadata().getName(),
-                    ready,
-                    project,
+                    ContainerStatus.Lifecycle.ready,
+                    projectId,
                     kubernetesService.environment,
-                    pod.getMetadata().getName().endsWith(DEVMODE_SUFFIX) ? ContainerStatus.CType.devmode : ContainerStatus.CType.pod,
+                    pod.getMetadata().getName().equals(projectId) ? ContainerStatus.CType.devmode : ContainerStatus.CType.project,
                     requestMemory + " : " + limitMemory,
                     requestCpu + " : " + limitCpu,
                     creationTimestamp
@@ -86,13 +89,7 @@ public class PodEventHandler implements ResourceEventHandler<Pod> {
             );
         } catch (Exception ex) {
             LOGGER.error(ex.getMessage(), ex.getCause());
-            return new ContainerStatus(
-                    pod.getMetadata().getName(),
-                    false,
-                    project,
-                    kubernetesService.environment,
-                    ContainerStatus.CType.pod,
-                    "");
+            return null;
         }
     }
 }
\ No newline at end of file
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 38570b9c..ba3afb33 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
@@ -26,9 +26,7 @@ import io.vertx.mutiny.ext.web.client.WebClient;
 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.infinispan.model.DevModeStatus;
 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;
@@ -39,8 +37,6 @@ import java.util.Arrays;
 import java.util.Objects;
 import java.util.concurrent.ExecutionException;
 
-import static org.apache.camel.karavan.shared.ConfigService.DEVMODE_SUFFIX;
-
 @ApplicationScoped
 public class CamelService {
 
@@ -74,13 +70,13 @@ public class CamelService {
 
     public void reloadProjectCode(String projectId) {
         LOGGER.info("Reload project code " + projectId);
-        String containerName = projectId + DEVMODE_SUFFIX;
         try {
-            infinispanService.getProjectFiles(projectId).forEach(projectFile -> putRequest(containerName, projectFile.getName(), projectFile.getCode(), 1000));
-            reloadRequest(containerName);
-            DevModeStatus dms = infinispanService.getDevModeStatus(projectId);
-            dms.setCodeLoaded(true);
-            infinispanService.saveDevModeStatus(dms);
+            ContainerStatus containerStatus = infinispanService.getDevModeContainerStatus(projectId, environment);
+            infinispanService.getProjectFiles(projectId).forEach(projectFile ->
+                    putRequest(projectId, projectFile.getName(), projectFile.getCode(), 1000));
+            reloadRequest(projectId);
+            containerStatus.setCodeLoaded(true);
+            infinispanService.saveContainerStatus(containerStatus);
         } catch (Exception ex) {
             LOGGER.error(ex.getMessage());
         }
@@ -116,7 +112,7 @@ public class CamelService {
     public void collectCamelStatuses() {
         if (infinispanService.isReady()) {
             infinispanService.getContainerStatuses(environment).forEach(pod -> {
-                CamelStatusRequest csr = new CamelStatusRequest(pod.getProjectId(), pod.getName());
+                CamelStatusRequest csr = new CamelStatusRequest(pod.getProjectId(), pod.getContainerName());
                 eventBus.publish(CMD_COLLECT_CAMEL_STATUS, JsonObject.mapFrom(csr));
             });
         }
@@ -135,43 +131,6 @@ public class CamelService {
         });
     }
 
-    public void cleanupDevModeStatuses() {
-        if (infinispanService.isReady()) {
-            infinispanService.getDevModeStatuses().forEach(dms -> {
-                ContainerStatus pod = infinispanService.getDevModeContainerStatuses(dms.getProjectId(), environment);
-                if (pod == null) {
-                    eventBus.publish(CMD_DELETE_CAMEL_STATUS, JsonObject.mapFrom(dms));
-                }
-            });
-        }
-    }
-
-    @ConsumeEvent(value = CMD_DELETE_CAMEL_STATUS, blocking = true, ordered = true)
-    public void cleanupDevModeStatus(JsonObject data) {
-        DevModeStatus dms = data.mapTo(DevModeStatus.class);
-        Arrays.stream(CamelStatus.Name.values()).forEach(name -> {
-            infinispanService.deleteCamelStatus(dms.getProjectId(), name.name(), environment);
-        });
-    }
-
-    private void reloadCode(String podName, String oldContext, String newContext) {
-        String projectName = podName.replace(DEVMODE_SUFFIX, "");
-        String newState = getContextState(newContext);
-        String oldState = getContextState(oldContext);
-        if (newContext != null && !Objects.equals(newState, oldState) && "Started".equals(newState)) {
-            reloadProjectCode(projectName);
-        }
-    }
-
-    private String getContextState(String context) {
-        if (context != null) {
-            JsonObject obj = new JsonObject(context);
-            return obj.getJsonObject("context").getString("state");
-        } else {
-            return null;
-        }
-    }
-
     public String getCamelStatus(String podName, CamelStatus.Name statusName) {
         String url = getContainerAddress(podName) + "/q/dev/" + statusName.name();
         try {
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 cc4277a8..06888997 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
@@ -5,7 +5,6 @@ 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.DevModeStatus;
 import org.apache.camel.karavan.infinispan.model.ContainerStatus;
 import org.apache.camel.karavan.kubernetes.KubernetesService;
 import org.apache.camel.karavan.shared.ConfigService;
@@ -56,7 +55,6 @@ public class EventService {
                 dockerService.startListeners();
                 dockerService.startInfinispan();
                 dockerService.checkInfinispanHealth();
-                dockerService.collectContainersStatuses();
             }
         } else {
             LOGGER.info("Starting Karavan in " + (kubernetesService.isOpenshift() ? "OpenShift" : "Kubernetes"));
@@ -67,12 +65,13 @@ public class EventService {
     @ConsumeEvent(value = INFINISPAN_STARTED, blocking = true, ordered = true)
     void startServices(String infinispanHealth) {
         if (infinispanHealth.equals(HEALTHY)) {
+            infinispanService.start(false);
+            infinispanService.clearAllStatuses();
             if (!ConfigService.inKubernetes()) {
                 dockerService.deleteKaravanHeadlessContainer();
                 dockerService.startKaravanHeadlessContainer();
+                dockerService.collectContainersStatuses();
             }
-            infinispanService.start(false);
-            infinispanService.clearAllStatuses();
             bus.publish(EventType.IMPORT_PROJECTS, "");
             bus.publish(EventType.START_INFRASTRUCTURE_LISTENERS, "");
         }
@@ -103,12 +102,16 @@ public class EventService {
         projectService.importProjects(data);
     }
 
-    @ConsumeEvent(value = DEVMODE_STATUS, blocking = true, ordered = true)
+    @ConsumeEvent(value = DEVMODE_CONTAINER_READY, blocking = true, ordered = true)
     void receiveCommand(JsonObject message) {
         LOGGER.info("received Status " + message);
-        DevModeStatus status = message.mapTo(DevModeStatus.class);
-        if (!status.getCodeLoaded() && status.getContainerId() != null) {
-            camelService.reloadProjectCode(status.getProjectId());
+        ContainerStatus status = message.mapTo(ContainerStatus.class);
+        if (!status.getCodeLoaded() && status.getContainerId() != null && status.getLifeCycle().equals(ContainerStatus.Lifecycle.ready)) {
+            if (ConfigService.inKubernetes()) {
+                camelService.reloadProjectCode(status.getProjectId());
+            } else {
+                infinispanService.sendCodeReloadCommand(status.getProjectId());
+            }
         }
     }
 
@@ -118,7 +121,7 @@ public class EventService {
         String memory = data.getString("memory");
         String cpu = data.getString("cpu");
         if (infinispanService.isReady()) {
-            ContainerStatus containerStatus = infinispanService.getDevModeContainerStatuses(projectId, configService.getConfiguration().getEnvironment());
+            ContainerStatus containerStatus = infinispanService.getDevModeContainerStatus(projectId, configService.getConfiguration().getEnvironment());
             if (containerStatus != null) {
                 containerStatus.setCpuInfo(cpu);
                 containerStatus.setMemoryInfo(memory);
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 ae586bbb..d7f32e95 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
@@ -18,6 +18,7 @@ package org.apache.camel.karavan.service;
 
 import io.quarkus.scheduler.Scheduled;
 import org.apache.camel.karavan.docker.DockerService;
+import org.apache.camel.karavan.shared.ConfigService;
 import org.jboss.logging.Logger;
 
 import javax.enterprise.context.ApplicationScoped;
@@ -45,10 +46,10 @@ public class ScheduledService {
     @Scheduled(every = "{karavan.camel.status.pull-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
     void collectCamelStatuses() {
         LOGGER.info("Collect info statuses");
-        // collect Camel statuses
-        camelService.collectCamelStatuses();
-        // clean DevMode statuses if container deleted
-        camelService.cleanupDevModeStatuses();
+        if (ConfigService.inKubernetes()) {
+            // collect Camel statuses
+            camelService.collectCamelStatuses();
+        }
     }
 
     @Scheduled(every = "{karavan.git.pull-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/ConfigService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/ConfigService.java
index 233c035d..e21bb8e1 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/ConfigService.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/ConfigService.java
@@ -29,7 +29,6 @@ import java.util.Objects;
 @ApplicationScoped
 public class ConfigService {
 
-    public static final String DEVMODE_SUFFIX = "-devmode";
     public static final String HEADLESS_MODE = "headless";
 
     @ConfigProperty(name = "karavan.version")
@@ -71,4 +70,8 @@ public class ConfigService {
     public static boolean isHeadless() {
         return ConfigUtils.isProfileActive(HEADLESS_MODE);
     }
+
+    public static boolean isDevOrTest() {
+        return ProfileManager.getLaunchMode().isDevOrTest();
+    }
 }
\ No newline at end of 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 08bc7fdb..37bdf5d3 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
@@ -14,6 +14,6 @@ public class EventType {
     public static final String INFINISPAN_STARTED = "INFINISPAN_STARTED";
 
     public static final String CONTAINER_STATISTICS = "CONTAINER_STATISTICS";
-    public static final String DEVMODE_STATUS = "DEVMODE_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 b0bfda46..76045045 100644
--- a/karavan-web/karavan-app/src/main/resources/application.properties
+++ b/karavan-web/karavan-app/src/main/resources/application.properties
@@ -6,7 +6,7 @@ karavan.runtimes=quarkus,spring-boot
 karavan.camel.status.pull-interval=off
 karavan.container.statistics.container=3s
 karavan.devmode.image=ghcr.io/apache/camel-karavan-runner:3.21.1-snapshot
-karavan.headless.image=marat/karavan-headless:3.21.1-SNAPSHOT
+karavan.headless.image=entropy1/karavan-headless:3.21.1-SNAPSHOT
 
 # Git repository Configuration
 karavan.git.repository=${GIT_REPOSITORY}
diff --git a/karavan-web/karavan-headless/src/main/docker/Dockerfile.distroless b/karavan-web/karavan-headless/src/main/docker/Dockerfile.distroless
index 2e9ab688..98eef01f 100644
--- a/karavan-web/karavan-headless/src/main/docker/Dockerfile.distroless
+++ b/karavan-web/karavan-headless/src/main/docker/Dockerfile.distroless
@@ -12,7 +12,7 @@
 #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
-FROM gcr.io/distroless/java:11
+FROM gcr.io/distroless/java@sha256:629d4fdc17eec821242d45497abcb88cc0442c47fd5748baa79d88dde7da3e2d
 COPY target/*-runner.jar /deployments/karavan.jar
 WORKDIR /deployments
 CMD ["karavan.jar"]
\ No newline at end of file
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 22372f0d..84621337 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
@@ -25,7 +25,6 @@ import io.vertx.mutiny.ext.web.client.WebClient;
 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.infinispan.model.DevModeStatus;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
 import org.jboss.logging.Logger;
@@ -36,8 +35,7 @@ import java.util.Arrays;
 import java.util.Objects;
 import java.util.concurrent.ExecutionException;
 
-import static org.apache.camel.karavan.headless.EventService.CMD_COLLECT_CAMEL_STATUS;
-import static org.apache.camel.karavan.headless.EventService.CMD_DELETE_CAMEL_STATUS;
+import static org.apache.camel.karavan.headless.EventService.*;
 
 @ApplicationScoped
 public class CamelService {
@@ -68,12 +66,12 @@ public class CamelService {
     public void reloadProjectCode(String projectId) {
         LOGGER.info("Reload project code " + projectId);
         try {
-            ContainerStatus containerStatus = infinispanService.getDevModeContainerStatuses(projectId, environment);
-            infinispanService.getProjectFiles(projectId).forEach(projectFile -> putRequest(projectId, projectFile.getName(), projectFile.getCode(), 1000));
+            ContainerStatus containerStatus = infinispanService.getDevModeContainerStatus(projectId, environment);
+            infinispanService.getProjectFiles(projectId).forEach(projectFile ->
+                    putRequest(projectId, projectFile.getName(), projectFile.getCode(), 1000));
             reloadRequest(projectId);
-            DevModeStatus dms = infinispanService.getDevModeStatus(projectId);
-            dms.setCodeLoaded(true);
-            infinispanService.saveDevModeStatus(dms);
+            containerStatus.setCodeLoaded(true);
+            infinispanService.saveContainerStatus(containerStatus);
         } catch (Exception ex) {
             LOGGER.error(ex.getMessage());
         }
@@ -102,19 +100,21 @@ public class CamelService {
     }
 
     public String getContainerAddress(String containerName) {
-        return "http://localhost:8080";
+        return "http://" + containerName + ":8080";
     }
 
     public void collectCamelStatuses() {
         if (infinispanService.isReady()) {
-            infinispanService.getContainerStatuses(environment).forEach(pod -> {
-                CamelStatusRequest csr = new CamelStatusRequest(pod.getProjectId(), pod.getName());
-                eventBus.publish(CMD_COLLECT_CAMEL_STATUS, JsonObject.mapFrom(csr));
-            });
+            infinispanService.getContainerStatuses(environment).stream()
+                    .filter(status -> status.getType().equals(ContainerStatus.CType.devmode) || status.getType().equals(ContainerStatus.CType.project))
+                    .forEach(status -> {
+                        CamelStatusRequest csr = new CamelStatusRequest(status.getProjectId(), status.getContainerName());
+                        eventBus.publish(CMD_COLLECT_CAMEL_STATUSES, JsonObject.mapFrom(csr));
+                    });
         }
     }
 
-    public void collectCamelStatuses(JsonObject data) {
+    public void collectCamelStatus(JsonObject data) {
         CamelStatusRequest dms = data.mapTo(CamelStatusRequest.class);
         Arrays.stream(CamelStatus.Name.values()).forEach(statusName -> {
             String containerName = dms.getContainerName();
@@ -126,43 +126,8 @@ public class CamelService {
         });
     }
 
-    public void cleanupDevModeStatuses() {
-        if (infinispanService.isReady()) {
-            infinispanService.getDevModeStatuses().forEach(dms -> {
-                ContainerStatus pod = infinispanService.getDevModeContainerStatuses(dms.getProjectId(), environment);
-                if (pod == null) {
-                    eventBus.publish(CMD_DELETE_CAMEL_STATUS, JsonObject.mapFrom(dms));
-                }
-            });
-        }
-    }
-
-    public void cleanupDevModeStatus(JsonObject data) {
-        DevModeStatus dms = data.mapTo(DevModeStatus.class);
-        Arrays.stream(CamelStatus.Name.values()).forEach(name -> {
-            infinispanService.deleteCamelStatus(dms.getProjectId(), name.name(), environment);
-        });
-    }
-
-    private void reloadCode(String podName, String oldContext, String newContext) {
-        String newState = getContextState(newContext);
-        String oldState = getContextState(oldContext);
-        if (newContext != null && !Objects.equals(newState, oldState) && "Started".equals(newState)) {
-            reloadProjectCode(podName);
-        }
-    }
-
-    private String getContextState(String context) {
-        if (context != null) {
-            JsonObject obj = new JsonObject(context);
-            return obj.getJsonObject("context").getString("state");
-        } else {
-            return null;
-        }
-    }
-
-    public String getCamelStatus(String podName, CamelStatus.Name statusName) {
-        String url = getContainerAddress(podName) + "/q/dev/" + statusName.name();
+    public String getCamelStatus(String containerName, CamelStatus.Name statusName) {
+        String url = getContainerAddress(containerName) + "/q/dev/" + statusName.name();
         try {
             return result(url, 500);
         } catch (InterruptedException | ExecutionException e) {
diff --git a/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/EventService.java b/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/EventService.java
index 234d99db..d21cf33d 100644
--- a/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/EventService.java
+++ b/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/EventService.java
@@ -19,7 +19,6 @@ package org.apache.camel.karavan.headless;
 import io.quarkus.vertx.ConsumeEvent;
 import io.vertx.core.json.JsonObject;
 import org.apache.camel.karavan.infinispan.InfinispanService;
-import org.jboss.logging.Logger;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
@@ -27,21 +26,20 @@ import javax.inject.Inject;
 @ApplicationScoped
 public class EventService {
 
-    private static final Logger LOGGER = Logger.getLogger(EventService.class.getName());
+    public static final String CMD_COLLECT_CAMEL_STATUSES = "collect-camel-statuses";
     public static final String CMD_COLLECT_CAMEL_STATUS = "collect-camel-status";
-    public static final String CMD_DELETE_CAMEL_STATUS = "delete-camel-status";
 
     @Inject
     CamelService camelService;
 
-    @ConsumeEvent(value = CMD_COLLECT_CAMEL_STATUS, blocking = true)
+    @ConsumeEvent(value = CMD_COLLECT_CAMEL_STATUSES, blocking = true)
     public void collectCamelStatuses(JsonObject data) {
         camelService.collectCamelStatuses();
     }
 
-    @ConsumeEvent(value = CMD_DELETE_CAMEL_STATUS, blocking = true)
-    public void cleanupDevModeStatus(JsonObject data) {
-        camelService.cleanupDevModeStatuses();
+    @ConsumeEvent(value = CMD_COLLECT_CAMEL_STATUS, blocking = true)
+    public void collectCamelStatus(JsonObject data) {
+        camelService.collectCamelStatus(data);
     }
 
     @ConsumeEvent(value = InfinispanService.CODE_RELOAD_COMMAND, blocking = true)
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 224f37f0..01b00da7 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
@@ -47,8 +47,6 @@ public class HeadlessService {
         LOGGER.info("Collect info statuses");
         // collect Camel statuses
         camelService.collectCamelStatuses();
-        // clean DevMode statuses if container deleted
-        camelService.cleanupDevModeStatuses();
     }
 
 }
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 36a9db6e..7b0e92e2 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
@@ -68,9 +68,7 @@ public class InfinispanService {
     private RemoteCache<GroupedKey, ServiceStatus> serviceStatuses;
     private RemoteCache<GroupedKey, CamelStatus> camelStatuses;
     private RemoteCache<String, String> commits;
-    private RemoteCache<GroupedKey, DevModeStatus> devmodeStatuses;
     private RemoteCache<GroupedKey, String> codeReloadCommands;
-    private RemoteCache<GroupedKey, ContainerInfo> containers;
     private final AtomicBoolean ready = new AtomicBoolean(false);
 
     private RemoteCacheManager cacheManager;
@@ -110,8 +108,6 @@ public class InfinispanService {
         camelStatuses = getOrCreateCache(CamelStatus.CACHE, false);
         commits = getOrCreateCache("commits", false);
         deploymentStatuses = getOrCreateCache(DeploymentStatus.CACHE, false);
-        devmodeStatuses = getOrCreateCache(DevModeStatus.CACHE, false);
-        containers = getOrCreateCache(ContainerInfo.CACHE, false);
         codeReloadCommands = getOrCreateCache("code_reload_commands", true);
 
         cacheManager.getCache(PROTOBUF_METADATA_CACHE_NAME).put("karavan.proto", getResourceFile("/proto/karavan.proto"));
@@ -262,14 +258,12 @@ public class InfinispanService {
                 .execute().list();
     }
 
-    public ContainerStatus getDevModeContainerStatuses(String projectId, String env) {
-        QueryFactory queryFactory = Search.getQueryFactory(containerStatuses);
-        List<ContainerStatus> list = queryFactory.<ContainerStatus>create("FROM karavan.ContainerStatus WHERE projectId = :projectId AND env = :env AND type = :type")
-                .setParameter("projectId", projectId)
-                .setParameter("env", env)
-                .setParameter("type", ContainerStatus.CType.devmode)
-                .execute().list();
-        return list.size() > 0 ? list.get(0) : null;
+    public ContainerStatus getContainerStatus(String projectId, String env, String containerName) {
+        return containerStatuses.get(GroupedKey.create(projectId, env, containerName));
+    }
+
+    public ContainerStatus getDevModeContainerStatus(String projectId, String env) {
+        return containerStatuses.get(GroupedKey.create(projectId, env, projectId));
     }
 
     public List<ContainerStatus> getContainerStatuses(String env) {
@@ -284,11 +278,11 @@ public class InfinispanService {
     }
 
     public void saveContainerStatus(ContainerStatus status) {
-        containerStatuses.put(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getName()), status);
+        containerStatuses.put(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getContainerName()), status);
     }
 
     public void deleteContainerStatus(ContainerStatus status) {
-        containerStatuses.remove(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getName()));
+        containerStatuses.remove(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getContainerName()));
     }
 
     public void deleteContainerStatus(String projectId, String env, String containerName) {
@@ -351,54 +345,34 @@ public class InfinispanService {
         return commits.get(commitId) != null;
     }
 
-    public void saveDevModeStatus(DevModeStatus status) {
-        devmodeStatuses.put(GroupedKey.create(status.getProjectId(), DEFAULT_ENVIRONMENT, status.getProjectId()), status);
-    }
-
-    public void deleteDevModeStatus(String projectId) {
-        devmodeStatuses.remove(GroupedKey.create(projectId, DEFAULT_ENVIRONMENT, projectId));
-    }
-
-    public DevModeStatus getDevModeStatus(String projectId) {
-        return devmodeStatuses.get(GroupedKey.create(projectId,DEFAULT_ENVIRONMENT, projectId));
-    }
 
-    public List<DevModeStatus> getLoadedDevModeStatuses() {
-        QueryFactory queryFactory = Search.getQueryFactory(devmodeStatuses);
-        return queryFactory.<DevModeStatus>create("FROM karavan.DevModeStatus WHERE codeLoaded = true")
+    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)
                 .execute().list();
     }
 
-    public List<DevModeStatus> getDevModeStatuses() {
-       return new ArrayList<>(devmodeStatuses.values());
-    }
-
-    public void saveContainerInfo(ContainerInfo ci) {
-        containers.put(GroupedKey.create(ci.getContainerName(), ci.getEnv() != null ? ci.getEnv() : DEFAULT_ENVIRONMENT, ci.getContainerName()), ci);
-    }
-
-    public void getContainerInfo(String name, String env) {
-        containers.get(GroupedKey.create(name, env, name));
+    public List<ContainerStatus> getDevModeStatuses() {
+        QueryFactory queryFactory = Search.getQueryFactory(containerStatuses);
+        return queryFactory.<ContainerStatus>create("FROM karavan.ContainerStatus WHERE type = :type")
+                .setParameter("type", ContainerStatus.CType.devmode)
+                .execute().list();
     }
 
-    public List<ContainerInfo> getContainerInfos(String env) {
-        QueryFactory queryFactory = Search.getQueryFactory(containers);
-        return queryFactory.<ContainerInfo>create("FROM karavan.ContainerInfo WHERE env = :env")
+    public List<ContainerStatus> getContainerStatusByEnv(String env) {
+        QueryFactory queryFactory = Search.getQueryFactory(containerStatuses);
+        return queryFactory.<ContainerStatus>create("FROM karavan.ContainerStatus WHERE env = :env")
                 .setParameter("env", env)
                 .execute().list();
     }
 
-    public void deleteContainerInfo(String containerName) {
-        containers.remove(GroupedKey.create(containerName, DEFAULT_ENVIRONMENT, containerName));
-    }
-
     public void clearAllStatuses() {
         CompletableFuture.allOf(
             deploymentStatuses.clearAsync(),
             containerStatuses.clearAsync(),
             pipelineStatuses.clearAsync(),
-            camelStatuses.clearAsync(),
-            devmodeStatuses.clearAsync()
+            camelStatuses.clearAsync()
         ).join();
     }
 
diff --git a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerInfo.java b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerInfo.java
deleted file mode 100644
index 985e70b3..00000000
--- a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerInfo.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package org.apache.camel.karavan.infinispan.model;
-
-import org.infinispan.protostream.annotations.ProtoFactory;
-import org.infinispan.protostream.annotations.ProtoField;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ContainerInfo {
-    public static final String CACHE = "container_infos";
-    @ProtoField(number = 1)
-    String containerName;
-    @ProtoField(number = 2)
-    String containerId;
-    @ProtoField(number = 3)
-    String image;
-    @ProtoField(number = 4, collectionImplementation = ArrayList.class)
-    List<Integer> ports;
-    @ProtoField(number = 5)
-    String env;
-
-    @ProtoFactory
-    public ContainerInfo(String containerName, String containerId, String image, List<Integer> ports, String env) {
-        this.containerName = containerName;
-        this.containerId = containerId;
-        this.image = image;
-        this.ports = ports;
-        this.env = env;
-    }
-
-    public String getEnv() {
-        return env;
-    }
-
-    public void setEnv(String env) {
-        this.env = env;
-    }
-
-    public String getContainerName() {
-        return containerName;
-    }
-
-    public void setContainerName(String containerName) {
-        this.containerName = containerName;
-    }
-
-    public String getContainerId() {
-        return containerId;
-    }
-
-    public void setContainerId(String containerId) {
-        this.containerId = containerId;
-    }
-
-    public String getImage() {
-        return image;
-    }
-
-    public void setImage(String image) {
-        this.image = image;
-    }
-
-    public List<Integer> getPorts() {
-        return ports;
-    }
-
-    public void setPorts(List<Integer> ports) {
-        this.ports = ports;
-    }
-}
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 f9204ec8..b994df0d 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
@@ -4,68 +4,100 @@ import org.infinispan.protostream.annotations.ProtoEnumValue;
 import org.infinispan.protostream.annotations.ProtoFactory;
 import org.infinispan.protostream.annotations.ProtoField;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class ContainerStatus {
 
     public enum CType {
-        @ProtoEnumValue(number = 0, name = "devmode") devmode,
-        @ProtoEnumValue(number = 1, name = "devservice") devservice,
-        @ProtoEnumValue(number = 2, name = "pod") pod,
-        @ProtoEnumValue(number = 3, name = "container") container,
+        @ProtoEnumValue(number = 0, name = "internal") internal,
+        @ProtoEnumValue(number = 1, name = "devmode") devmode,
+        @ProtoEnumValue(number = 2, name = "devservice") devservice,
+        @ProtoEnumValue(number = 4, name = "project") project,
+        @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 static final String CACHE = "pod_statuses";
+    public static final String CACHE = "container_statuses";
     @ProtoField(number = 1)
-    String name;
+    String projectId;
     @ProtoField(number = 2)
-    Boolean ready;
+    String containerName;
     @ProtoField(number = 3)
-    String projectId;
+    String containerId;
     @ProtoField(number = 4)
+    String image;
+    @ProtoField(number = 5, collectionImplementation = ArrayList.class)
+    List<Integer> ports;
+    @ProtoField(number = 6)
     String env;
-    @ProtoField(number = 5)
+    @ProtoField(number = 7)
     CType type;
-    @ProtoField(number = 6)
+    @ProtoField(number = 8)
     String memoryInfo;
-    @ProtoField(number = 7)
+    @ProtoField(number = 9)
     String cpuInfo;
-    @ProtoField(number = 8)
+    @ProtoField(number = 10)
     String created;
+    @ProtoField(number = 11)
+    Lifecycle lifeCycle;
+    @ProtoField(number = 12)
+    Boolean codeLoaded;
+    @ProtoField(number = 13)
+    Boolean logging;
 
     @ProtoFactory
-    public ContainerStatus(String name, Boolean ready, String projectId, String env, CType type, String memoryInfo, String cpuInfo, String created) {
-        this.name = name;
-        this.ready = ready;
+    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) {
         this.projectId = projectId;
+        this.containerName = containerName;
+        this.containerId = containerId;
+        this.image = image;
+        this.ports = ports;
         this.env = env;
         this.type = type;
         this.memoryInfo = memoryInfo;
         this.cpuInfo = cpuInfo;
         this.created = created;
+        this.lifeCycle = lifeCycle;
+        this.codeLoaded = codeLoaded;
+        this.logging = logging;
     }
 
-    public ContainerStatus(String name, Boolean ready, String projectId, String env, CType type, String created) {
-        this.name = name;
-        this.ready = ready;
+    public ContainerStatus(String containerName, Lifecycle lifeCycle, String projectId, String env, CType type, String memoryInfo, String cpuInfo, String created) {
+        this.containerName = containerName;
+        this.lifeCycle = lifeCycle;
         this.projectId = projectId;
         this.env = env;
-        this.created = created;
         this.type = type;
+        this.memoryInfo = memoryInfo;
+        this.cpuInfo = cpuInfo;
+        this.created = created;
     }
 
-    public String getName() {
-        return name;
+    public ContainerStatus(String containerName, Lifecycle lifeCycle, String projectId, String env, CType type, String created) {
+        this.containerName = containerName;
+        this.lifeCycle = lifeCycle;
+        this.projectId = projectId;
+        this.env = env;
+        this.created = created;
+        this.type = type;
     }
 
-    public void setName(String name) {
-        this.name = name;
+    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);
     }
 
-    public Boolean getReady() {
-        return ready;
+    public static ContainerStatus createWithId(String name, String env, String containerId, List<Integer> ports, CType type, Lifecycle lifeCycle) {
+        return new ContainerStatus(name, name, containerId, null, ports, env, type,
+                null, null, null,  lifeCycle, false, false);
     }
 
-    public void setReady(Boolean ready) {
-        this.ready = ready;
+    public ContainerStatus() {
     }
 
     public String getProjectId() {
@@ -76,6 +108,38 @@ public class ContainerStatus {
         this.projectId = projectId;
     }
 
+    public String getContainerName() {
+        return containerName;
+    }
+
+    public void setContainerName(String containerName) {
+        this.containerName = containerName;
+    }
+
+    public String getContainerId() {
+        return containerId;
+    }
+
+    public void setContainerId(String containerId) {
+        this.containerId = containerId;
+    }
+
+    public String getImage() {
+        return image;
+    }
+
+    public void setImage(String image) {
+        this.image = image;
+    }
+
+    public List<Integer> getPorts() {
+        return ports;
+    }
+
+    public void setPorts(List<Integer> ports) {
+        this.ports = ports;
+    }
+
     public String getEnv() {
         return env;
     }
@@ -84,6 +148,14 @@ public class ContainerStatus {
         this.env = env;
     }
 
+    public CType getType() {
+        return type;
+    }
+
+    public void setType(CType type) {
+        this.type = type;
+    }
+
     public String getMemoryInfo() {
         return memoryInfo;
     }
@@ -108,11 +180,27 @@ public class ContainerStatus {
         this.created = created;
     }
 
-    public CType getType() {
-        return type;
+    public Lifecycle getLifeCycle() {
+        return lifeCycle;
     }
 
-    public void setType(CType type) {
-        this.type = type;
+    public void setLifeCycle(Lifecycle lifeCycle) {
+        this.lifeCycle = lifeCycle;
+    }
+
+    public Boolean getCodeLoaded() {
+        return codeLoaded;
+    }
+
+    public void setCodeLoaded(Boolean codeLoaded) {
+        this.codeLoaded = codeLoaded;
+    }
+
+    public Boolean getLogging() {
+        return logging;
+    }
+
+    public void setLogging(Boolean logging) {
+        this.logging = logging;
     }
 }
diff --git a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/DevModeStatus.java b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/DevModeStatus.java
deleted file mode 100644
index c45208d3..00000000
--- a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/DevModeStatus.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package org.apache.camel.karavan.infinispan.model;
-
-import org.infinispan.protostream.annotations.ProtoFactory;
-import org.infinispan.protostream.annotations.ProtoField;
-
-public class DevModeStatus {
-    public static final String CACHE = "devmode_statuses";
-    @ProtoField(number = 1)
-    String projectId;
-    @ProtoField(number = 2)
-    String containerName;
-    @ProtoField(number = 3)
-    String containerId;
-    @ProtoField(number = 4)
-    Boolean codeLoaded;
-    @ProtoField(number = 5)
-    Boolean logging;
-
-    @ProtoFactory
-    public DevModeStatus(String projectId, String containerName, String containerId, Boolean codeLoaded, Boolean logging) {
-        this.projectId = projectId;
-        this.containerName = containerName;
-        this.containerId = containerId;
-        this.codeLoaded = codeLoaded;
-        this.logging = logging;
-    }
-
-    public DevModeStatus(String projectId, String containerName, String containerId, Boolean codeLoaded) {
-        this.projectId = projectId;
-        this.containerName = containerName;
-        this.containerId = containerId;
-        this.codeLoaded = codeLoaded;
-        this.logging = false;
-    }
-
-    public DevModeStatus() {
-    }
-
-    public String getProjectId() {
-        return projectId;
-    }
-
-    public void setProjectId(String projectId) {
-        this.projectId = projectId;
-    }
-
-    public String getContainerName() {
-        return containerName;
-    }
-
-    public void setContainerName(String containerName) {
-        this.containerName = containerName;
-    }
-
-    public String getContainerId() {
-        return containerId;
-    }
-
-    public void setContainerId(String containerId) {
-        this.containerId = containerId;
-    }
-
-    public Boolean getCodeLoaded() {
-        return codeLoaded;
-    }
-
-    public void setCodeLoaded(Boolean codeLoaded) {
-        this.codeLoaded = codeLoaded;
-    }
-
-    public Boolean getLogging() {
-        return logging;
-    }
-
-    public void setLogging(Boolean logging) {
-        this.logging = logging;
-    }
-}
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 1b2777ca..c20115b3 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
@@ -15,9 +15,8 @@ import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;
                 DeploymentStatus.class,
                 ContainerStatus.class,
                 ContainerStatus.CType.class,
-                ServiceStatus.class,
-                DevModeStatus.class,
-                ContainerInfo.class
+                ContainerStatus.Lifecycle.class,
+                ServiceStatus.class
         },
         schemaFileName = "karavan.proto",
         schemaFilePath = "proto/",
diff --git a/karavan-web/karavan-infinispan/src/test/java/org/apache/camel/karavan/infinispan/DataGridTest.java b/karavan-web/karavan-infinispan/src/test/java/org/apache/camel/karavan/infinispan/DataGridTest.java
index d90d4bcc..0aa7914b 100644
--- a/karavan-web/karavan-infinispan/src/test/java/org/apache/camel/karavan/infinispan/DataGridTest.java
+++ b/karavan-web/karavan-infinispan/src/test/java/org/apache/camel/karavan/infinispan/DataGridTest.java
@@ -3,9 +3,8 @@ package org.apache.camel.karavan.infinispan;
 
 import io.quarkus.test.junit.QuarkusTest;
 import io.quarkus.vertx.ConsumeEvent;
-import io.vertx.core.json.JsonObject;
 import org.apache.camel.karavan.infinispan.model.CamelStatus;
-import org.apache.camel.karavan.infinispan.model.ContainerInfo;
+import org.apache.camel.karavan.infinispan.model.ContainerStatus;
 import org.apache.camel.karavan.infinispan.model.ProjectFile;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
@@ -31,14 +30,6 @@ public class DataGridTest {
         infinispanService.start(true);
     }
 
-    @Test
-    public void testContainersStatuses() throws InterruptedException {
-        ContainerInfo ci = new ContainerInfo("demo", "id", "image", List.of(8080, 8081, 8082), "dev");
-        infinispanService.saveContainerInfo(ci);
-        List<ContainerInfo> list = infinispanService.getContainerInfos("dev");
-        assertEquals(1, list.size());
-    }
-
     @Test
     public void testProjectFiles() throws InterruptedException {
         List<ProjectFile> files = infinispanService.getProjectFiles("xxx");
diff --git a/karavan-web/pom.xml b/karavan-web/pom.xml
index 54d6d283..124eb8c5 100644
--- a/karavan-web/pom.xml
+++ b/karavan-web/pom.xml
@@ -35,7 +35,7 @@
         <slf4j-api-version>2.0.6</slf4j-api-version>
         <log4j2-version>2.20.0</log4j2-version>
 
-        <docker-java.version>3.2.7</docker-java.version>
+        <docker-java.version>3.2.13</docker-java.version>
 
         <maven-shade-plugin.version>3.4.1</maven-shade-plugin.version>
         <maven-jar-plugin.version>3.3.0</maven-jar-plugin.version>


[camel-karavan] 02/02: Working prototype #817

Posted by ma...@apache.org.
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 a26b5660eacddfd4bd2b547f7bfad957b437b70d
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Sat Jul 22 20:54:17 2023 -0400

    Working prototype #817
---
 .github/workflows/app.yml                          | 17 +++++--
 .../workflows/{builder.yml => docker-builder.yml}  |  0
 .../workflows/{runner.yml => docker-devmode.yml}   |  2 +-
 .github/workflows/headless.yml                     | 56 ++++++++++++++++++++++
 .../apache/camel/karavan/api/DevModeResource.java  | 10 ++--
 .../apache/camel/karavan/api/KameletResources.java | 14 +++---
 .../camel/karavan/docker/DockerEventListener.java  |  5 +-
 .../apache/camel/karavan/docker/DockerService.java |  2 +-
 .../karavan/kubernetes/KubernetesService.java      |  4 +-
 .../apache/camel/karavan/service/EventService.java | 31 +-----------
 .../camel/karavan/service/KaravanService.java      | 37 ++++++++++++--
 .../org/apache/camel/karavan/shared/EventType.java |  3 --
 .../karavan-app/src/main/webui/src/Main.tsx        |  4 +-
 .../src/main/webui/src/api/KaravanApi.tsx          |  6 +--
 .../src/main/webui/src/api/ProjectModels.ts        | 12 +++--
 .../src/main/webui/src/api/ProjectService.ts       | 10 ++--
 .../src/main/webui/src/api/ProjectStore.ts         |  6 +--
 .../src/main/webui/src/project/DevModeToolbar.tsx  | 22 +++++++--
 .../webui/src/project/dashboard/DashboardTab.tsx   |  8 ++--
 .../dashboard/{InfoPod.tsx => InfoContainer.tsx}   | 23 ++++-----
 .../webui/src/project/pipeline/ProjectStatus.tsx   | 18 +++----
 .../src/main/webui/src/project/trace/TraceTab.tsx  |  1 +
 .../camel/karavan/headless/CamelService.java       |  2 +-
 .../camel/karavan/headless/EventService.java       |  6 ---
 .../karavan/infinispan/InfinispanService.java      |  2 +
 .../karavan/infinispan/model/ContainerStatus.java  |  4 +-
 26 files changed, 191 insertions(+), 114 deletions(-)

diff --git a/.github/workflows/app.yml b/.github/workflows/app.yml
index 5eb961ae..ee3361ee 100644
--- a/.github/workflows/app.yml
+++ b/.github/workflows/app.yml
@@ -1,9 +1,9 @@
-name: cloud-native app 
+name: web app 
 
 on:
   push:
     branches: [ main ]
-    paths: ['karavan-web/karavan-app/**', 'karavan-core/**', 'karavan-designer/**']
+    paths: ['karavan-web/karavan-app/**', 'karavan-web/karavan-infinispan/**', 'karavan-core/**', 'karavan-designer/**']
   workflow_dispatch:
   pull_request:
     branches: [ main ]
@@ -51,7 +51,14 @@ jobs:
         working-directory: ./karavan/karavan-core
         run: npm ci
 
-      #  Build Karavan cloud app with public access
+      #  Build Karavan infinispan module
+      - name: Build infinispan module
+        working-directory: ./karavan/karavan-web/karavan-infinispan
+        run: |
+          mvn package
+        if: ${{ github.ref == 'refs/heads/main' }}
+
+      #  Build Karavan web app with public access
       - name: Build application public
         working-directory: ./karavan/karavan-web/karavan-app
         run: |
@@ -64,7 +71,7 @@ jobs:
           -Dquarkus.container-image.password=${{ secrets.GITHUB_TOKEN }}
         if: ${{ github.ref == 'refs/heads/main' }}
 
-      #  Build Karavan cloud app with basic authorization
+      #  Build Karavan web app with basic authorization
       - name: Build application basic auth
         working-directory: ./karavan/karavan-web/karavan-app
         run: |
@@ -77,7 +84,7 @@ jobs:
           -Dquarkus.container-image.password=${{ secrets.GITHUB_TOKEN }}
         if: ${{ github.ref == 'refs/heads/main' }}
 
-      #  Build Karavan cloud app with oidc authorization
+      #  Build Karavan web app with oidc authorization
       - name: Build application oidc
         working-directory: ./karavan/karavan-web/karavan-app
         run: |
diff --git a/.github/workflows/builder.yml b/.github/workflows/docker-builder.yml
similarity index 100%
rename from .github/workflows/builder.yml
rename to .github/workflows/docker-builder.yml
diff --git a/.github/workflows/runner.yml b/.github/workflows/docker-devmode.yml
similarity index 97%
rename from .github/workflows/runner.yml
rename to .github/workflows/docker-devmode.yml
index af8d4a80..695bdacc 100644
--- a/.github/workflows/runner.yml
+++ b/.github/workflows/docker-devmode.yml
@@ -1,4 +1,4 @@
-name: runner image
+name: DevMode image
 
 on:
   push:
diff --git a/.github/workflows/headless.yml b/.github/workflows/headless.yml
new file mode 100644
index 00000000..66bc013c
--- /dev/null
+++ b/.github/workflows/headless.yml
@@ -0,0 +1,56 @@
+name: headless service 
+
+on:
+  push:
+    branches: [ main ]
+    paths: ['karavan-web/karavan-headless/**', 'karavan-web/karavan-infinispan/**']
+  workflow_dispatch:
+  pull_request:
+    branches: [ main ]
+
+env:
+  TAG: 3.21.1-SNAPSHOT
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout Karavan
+        uses: actions/checkout@v3
+        with:
+          path: karavan
+
+      - name: Set up JDK 11
+        uses: actions/setup-java@v3
+        with:
+          distribution: adopt
+          java-version: 11
+
+      - name: Cache local Maven repository
+        uses: actions/cache@v3
+        with:
+          path: |
+            ~/.m2/repository
+            !~/.m2/repository/org/apache/camel/karavan
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+          restore-keys: ${{ runner.os }}-maven-
+
+      #  Build Karavan infinispan module
+      - name: Build infinispan module
+        working-directory: ./karavan/karavan-web/karavan-infinispan
+        run: |
+          mvn package
+        if: ${{ github.ref == 'refs/heads/main' }}
+
+      #  Build Karavan headless 
+      - name: Build headless service
+        working-directory: ./karavan/karavan-web/karavan-headless
+        run: |
+          mvn package -DskipTests \
+          -Dquarkus.container-image.build=true \
+          -Dquarkus.container-image.push=true \
+          -Dquarkus.container-image.image=ghcr.io/${GITHUB_REPOSITORY}-headless:${{ env.TAG }} \
+          -Dquarkus.container-image.username=${{ github.actor }} \
+          -Dquarkus.container-image.password=${{ secrets.GITHUB_TOKEN }}
+        if: ${{ github.ref == 'refs/heads/main' }}
+
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 c3d6184c..13edee0e 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
@@ -127,9 +127,13 @@ public class DevModeResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/status/{projectId}/{statusName}")
     public Response getCamelStatusByProjectAndEnv(@PathParam("projectId") String projectId, @PathParam("statusName") String statusName) {
-        CamelStatus status = infinispanService.getCamelStatus(projectId, environment, statusName);
-        if (status != null) {
-            return Response.ok(status).build();
+        if (infinispanService.isReady()) {
+            CamelStatus status = infinispanService.getCamelStatus(projectId, environment, statusName);
+            if (status != null) {
+                return Response.ok(status).build();
+            } else {
+                return Response.noContent().build();
+            }
         } else {
             return Response.noContent().build();
         }
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/KameletResources.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/KameletResources.java
index ba8ccab1..ac2b8a67 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/KameletResources.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/KameletResources.java
@@ -45,12 +45,14 @@ public class KameletResources {
     @Produces(MediaType.TEXT_PLAIN)
     public String getKamelets() {
         StringBuilder kamelets = new StringBuilder(codeService.getResourceFile("/kamelets/kamelets.yaml"));
-        List<ProjectFile> custom = infinispanService.getProjectFiles(Project.Type.kamelets.name());
-        if (custom.size() > 0) {
-            kamelets.append("\n---\n");
-            kamelets.append(custom.stream()
-                    .map(ProjectFile::getCode)
-                    .collect(Collectors.joining("\n---\n")));
+        if (infinispanService.isReady()) {
+            List<ProjectFile> custom = infinispanService.getProjectFiles(Project.Type.kamelets.name());
+            if (custom.size() > 0) {
+                kamelets.append("\n---\n");
+                kamelets.append(custom.stream()
+                        .map(ProjectFile::getCode)
+                        .collect(Collectors.joining("\n---\n")));
+            }
         }
         return kamelets.toString();
     }
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 4a9bd265..0ee8000a 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
@@ -16,6 +16,7 @@ import javax.enterprise.context.ApplicationScoped;
 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;
@@ -92,14 +93,16 @@ public class DockerEventListener implements ResultCallback<Event> {
         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(), ports, type, lc);
+            ci = ContainerStatus.createWithId(name, environment, container.getId(), ports, type, lc, created);
         } else {
             ci.setContainerId(container.getId());
             ci.setPorts(ports);
             ci.setType(type);
             ci.setLifeCycle(lc);
+            ci.setCreated(created);
         }
         infinispanService.saveContainerStatus(ci);
     }
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 76ed2c62..3b380749 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
@@ -171,7 +171,7 @@ public class DockerService {
                     .withDriver("bridge")
                     .withInternal(false)
                     .withAttachable(true).exec();
-            LOGGER.info("Network created: {}" + res);
+            LOGGER.info("Network created: " + NETWORK_NAME);
         } else {
             LOGGER.info("Network already exists with name: " + NETWORK_NAME);
         }
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 e1f231b2..92971460 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
@@ -95,7 +95,7 @@ public class KubernetesService implements HealthCheck {
 
     public void startInformers(String data) {
         try {
-            stopInformers(null);
+            stopInformers();
             LOGGER.info("Starting Kubernetes Informers");
 
             SharedIndexInformer<Deployment> deploymentInformer = kubernetesClient().apps().deployments().inNamespace(getNamespace())
@@ -138,7 +138,7 @@ public class KubernetesService implements HealthCheck {
         }
     }
 
-    public void stopInformers(String data) {
+    public void stopInformers() {
         LOGGER.info("Stop Kubernetes Informers");
         informers.forEach(SharedIndexInformer::close);
         informers.clear();
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 06888997..fe48ad1c 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
@@ -21,7 +21,6 @@ import static org.apache.camel.karavan.shared.EventType.*;
 public class EventService {
 
     private static final Logger LOGGER = Logger.getLogger(EventService.class.getName());
-    private static final String HEALTHY = "healthy";
 
     @Inject
     InfinispanService infinispanService;
@@ -44,27 +43,9 @@ public class EventService {
     @Inject
     EventBus bus;
 
-    @ConsumeEvent(value = START_KARAVAN, blocking = true, ordered = true)
-    void startKaravan(String data) {
-        if (!ConfigService.inKubernetes()) {
-            if (ConfigService.isHeadless()) {
-                LOGGER.info("Starting Karavan Headless in Docker");
-            } else {
-                LOGGER.info("Starting Karavan with Docker");
-                dockerService.createNetwork();
-                dockerService.startListeners();
-                dockerService.startInfinispan();
-                dockerService.checkInfinispanHealth();
-            }
-        } else {
-            LOGGER.info("Starting Karavan in " + (kubernetesService.isOpenshift() ? "OpenShift" : "Kubernetes"));
-            startServices(HEALTHY);
-        }
-    }
-
     @ConsumeEvent(value = INFINISPAN_STARTED, blocking = true, ordered = true)
     void startServices(String infinispanHealth) {
-        if (infinispanHealth.equals(HEALTHY)) {
+        if (infinispanHealth.equals(InfinispanService.HEALTHY_STATUS)) {
             infinispanService.start(false);
             infinispanService.clearAllStatuses();
             if (!ConfigService.inKubernetes()) {
@@ -87,16 +68,6 @@ public class EventService {
         }
     }
 
-    @ConsumeEvent(value = STOP_INFRASTRUCTURE_LISTENERS, blocking = true)
-    void stopInfrastructureListeners(String data) throws IOException {
-        LOGGER.info("Stop Infrastructure Listeners");
-        if (ConfigService.inKubernetes()) {
-            kubernetesService.stopInformers(data);
-        } else {
-            dockerService.stopListeners();
-        }
-    }
-
     @ConsumeEvent(value = IMPORT_PROJECTS, blocking = true)
     public void importProjects(String data) {
         projectService.importProjects(data);
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 12a1c409..2f5aa7ad 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
@@ -19,31 +19,58 @@ package org.apache.camel.karavan.service;
 import io.quarkus.runtime.ShutdownEvent;
 import io.quarkus.runtime.StartupEvent;
 import io.vertx.core.eventbus.EventBus;
+import org.apache.camel.karavan.docker.DockerService;
+import org.apache.camel.karavan.infinispan.InfinispanService;
+import org.apache.camel.karavan.kubernetes.KubernetesService;
+import org.apache.camel.karavan.shared.ConfigService;
 import org.apache.camel.karavan.shared.EventType;
 import org.jboss.logging.Logger;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.enterprise.event.Observes;
 import javax.inject.Inject;
-
-import static org.apache.camel.karavan.shared.EventType.START_KARAVAN;
+import java.io.IOException;
 
 @ApplicationScoped
 public class KaravanService {
 
     private static final Logger LOGGER = Logger.getLogger(KaravanService.class.getName());
 
+    @Inject
+    KubernetesService kubernetesService;
+
+    @Inject
+    DockerService dockerService;
+
     @Inject
     EventBus bus;
 
     void onStart(@Observes StartupEvent ev) {
         LOGGER.info("Starting Karavan");
-        bus.publish(START_KARAVAN, "");
+        if (!ConfigService.inKubernetes()) {
+            if (ConfigService.isHeadless()) {
+                LOGGER.info("Starting Karavan Headless in Docker");
+            } else {
+                LOGGER.info("Starting Karavan with Docker");
+                dockerService.createNetwork();
+                dockerService.startListeners();
+                dockerService.startInfinispan();
+                dockerService.checkInfinispanHealth();
+            }
+        } else {
+            LOGGER.info("Starting Karavan in " + (kubernetesService.isOpenshift() ? "OpenShift" : "Kubernetes"));
+            bus.publish(EventType.INFINISPAN_STARTED, InfinispanService.HEALTHY_STATUS);
+        }
     }
 
-    void onStop(@Observes ShutdownEvent ev) {
+    void onStop(@Observes ShutdownEvent ev) throws IOException  {
+        LOGGER.info("Stop Listeners");
+        if (ConfigService.inKubernetes()) {
+            kubernetesService.stopInformers();
+        } else {
+            dockerService.stopListeners();
+        }
         LOGGER.info("Stop Karavan");
-        bus.publish(EventType.STOP_INFRASTRUCTURE_LISTENERS, "");
     }
 
 }
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 37bdf5d3..ac89a948 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
@@ -2,11 +2,8 @@ package org.apache.camel.karavan.shared;
 
 public class EventType {
 
-    public static final String START_KARAVAN = "START_KARAVAN";
-
     //    Start Kubernetes or Docker event Listeners
     public static final String START_INFRASTRUCTURE_LISTENERS = "START_INFRASTRUCTURE_LISTENERS";
-    public static final String STOP_INFRASTRUCTURE_LISTENERS = "STOP_INFRASTRUCTURE_LISTENERS";
 
     //    Import projects from Git repository
     public static final String IMPORT_PROJECTS = "IMPORT_PROJECTS";
diff --git a/karavan-web/karavan-app/src/main/webui/src/Main.tsx b/karavan-web/karavan-app/src/main/webui/src/Main.tsx
index a5a14a9b..a9f46911 100644
--- a/karavan-web/karavan-app/src/main/webui/src/Main.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/Main.tsx
@@ -26,7 +26,7 @@ import {MainLogin} from "./MainLogin";
 import {DashboardPage} from "./dashboard/DashboardPage";
 import {Subscription} from "rxjs";
 import {ProjectEventBus} from "./api/ProjectEventBus";
-import {AppConfig, PodStatus, Project, ToastMessage} from "./api/ProjectModels";
+import {AppConfig, ContainerStatus, Project, ToastMessage} from "./api/ProjectModels";
 import {ProjectPage} from "./project/ProjectPage";
 import {useAppConfigStore, useDevModeStore, useFileStore, useProjectStore} from "./api/ProjectStore";
 import {Notification} from "./Notification";
@@ -179,7 +179,7 @@ export class Main extends React.Component<Props, State> {
                                 onClick={event => {
                                     useFileStore.setState({operation:'none', file: undefined})
                                     useDevModeStore.setState({podName: undefined, status: "none"})
-                                    useProjectStore.setState({podStatus: new PodStatus({}), })
+                                    useProjectStore.setState({containerStatus: new ContainerStatus({}), })
                                     this.setState({pageId: page.pageId});
                                 }}
                         />
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 d9290f72..6515e83a 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
@@ -4,7 +4,7 @@ import {
     CamelStatus,
     DeploymentStatus,
     PipelineStatus,
-    PodStatus,
+    ContainerStatus,
     Project,
     ProjectFile, ProjectType, ServiceStatus
 } from "./ProjectModels";
@@ -303,7 +303,7 @@ export class KaravanApi {
         });
     }
 
-    static async getDevModePodStatus(projectId: string, after: (res: AxiosResponse<PodStatus>) => void) {
+    static async getDevModePodStatus(projectId: string, after: (res: AxiosResponse<ContainerStatus>) => void) {
         instance.get('/api/devmode/pod/' + projectId)
             .then(res => {
                 after(res);
@@ -441,7 +441,7 @@ export class KaravanApi {
         });
     }
 
-    static async getProjectPodStatuses(project: string, env: string, after: (statuses: PodStatus[]) => void) {
+    static async getProjectPodStatuses(project: string, env: string, after: (statuses: ContainerStatus[]) => void) {
         instance.get('/api/infrastructure/pod/' + project + "/" + env)
             .then(res => {
                 if (res.status === 200) {
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 3c758d3e..73be3256 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
@@ -67,18 +67,20 @@ export class ServiceStatus {
     type: string = '';
 }
 
-export class PodStatus {
-    name: string = '';
-    ready: boolean = false;
+export class ContainerStatus {
+    containerName: string = '';
+    lifeCycle: string = '';
     deployment: string = '';
     projectId: string = '';
     env: string = '';
-    inDevMode: boolean = false;
+    type: string = '';
     memoryInfo: string = '';
     cpuInfo: string = '';
     created: string = '';
+    image: string = '';
+    ports: [] = [];
 
-    public constructor(init?: Partial<PodStatus>) {
+    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 a83e8327..4e6621cc 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
@@ -1,5 +1,5 @@
 import {KaravanApi} from "./KaravanApi";
-import {DeploymentStatus, PodStatus, Project, ProjectFile, ToastMessage} from "./ProjectModels";
+import {DeploymentStatus, ContainerStatus, Project, ProjectFile, ToastMessage} from "./ProjectModels";
 import {TemplateApi} from "karavan-core/lib/api/TemplateApi";
 import {InfrastructureAPI} from "../designer/utils/InfrastructureAPI";
 import {unstable_batchedUpdates} from 'react-dom'
@@ -57,20 +57,20 @@ export class ProjectService {
             if (res.status === 200) {
                 unstable_batchedUpdates(() => {
                     const podStatus = res.data;
-                    if (useDevModeStore.getState().podName !== podStatus.name){
-                        useDevModeStore.setState({podName: podStatus.name})
+                    if (useDevModeStore.getState().podName !== podStatus.containerName){
+                        useDevModeStore.setState({podName: podStatus.containerName})
                     }
                     if (useDevModeStore.getState().status !== "running"){
                         useDevModeStore.setState({status: "running"})
                         useLogStore.setState({isRunning: true})
                     }
-                    useProjectStore.setState({podStatus: res.data});
+                    useProjectStore.setState({containerStatus: res.data});
                 })
             } else {
                 unstable_batchedUpdates(() => {
                     if (useDevModeStore.getState().status !== 'none') {
                         useDevModeStore.setState({status: "none", podName: undefined})
-                        useProjectStore.setState({podStatus: new PodStatus()});
+                        useProjectStore.setState({containerStatus: new ContainerStatus()});
                     }
                 })
             }
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 d04fa3c2..9a891b3e 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
@@ -16,7 +16,7 @@
  */
 
 import {create} from 'zustand'
-import {AppConfig, DeploymentStatus, PodStatus, Project, ProjectFile, ToastMessage} from "./ProjectModels";
+import {AppConfig, DeploymentStatus, ContainerStatus, Project, ProjectFile, ToastMessage} from "./ProjectModels";
 import {ProjectEventBus} from "./ProjectEventBus";
 import {unstable_batchedUpdates} from "react-dom";
 import {bottom} from "@patternfly/react-core/helpers/Popper/thirdparty/popper-core";
@@ -60,7 +60,7 @@ interface ProjectState {
     project: Project;
     isPushing: boolean,
     isRunning: boolean,
-    podStatus: PodStatus,
+    containerStatus: ContainerStatus,
     operation: "create" | "select" | "delete" | "none" | "copy";
     setProject: (project: Project, operation:  "create" | "select" | "delete"| "none" | "copy") => void;
     setOperation: (o: "create" | "select" | "delete"| "none" | "copy") => void;
@@ -71,7 +71,7 @@ export const useProjectStore = create<ProjectState>((set) => ({
     operation: "none",
     isPushing: false,
     isRunning: false,
-    podStatus: new PodStatus(),
+    containerStatus: new ContainerStatus(),
     setProject: (project: Project, operation:  "create" | "select" | "delete"| "none" | "copy") => {
         set((state: ProjectState) => ({
             project: project,
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 99bff60e..4f202b92 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
@@ -8,6 +8,7 @@ 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";
 
 
 interface Props {
@@ -17,21 +18,34 @@ interface Props {
 export const DevModeToolbar = (props: Props) => {
 
     const [status] = useDevModeStore((state) => [state.status], shallow)
-    const [project,podStatus ] = useProjectStore((state) => [state.project, state.podStatus], 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";
     return (<Flex className="toolbar" direction={{default: "row"}} alignItems={{default: "alignItemsCenter"}}>
         {isRunning && <FlexItem>
-            <Label icon={<UpIcon/>} color={"green"}>
+            <Label icon={getIcon()} color={getColor()}>
                 <Tooltip content={"Show log"} position={TooltipPosition.bottom}>
                     <Button variant="link"
                             onClick={e =>
-                                useLogStore.setState({showLog: true, type: 'container', podName: podStatus.name})}>
-                        {podStatus.name}
+                                useLogStore.setState({showLog: true, type: 'container', podName: containerStatus.containerName})}>
+                        {containerStatus.containerName}
                     </Button>
                 </Tooltip>
             </Label>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx
index 1831dd65..9aa0a0f8 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx
@@ -20,7 +20,7 @@ import {
     CardBody, Flex, FlexItem, Divider, PageSection
 } from '@patternfly/react-core';
 import '../../designer/karavan.css';
-import {InfoPod} from "./InfoPod";
+import {InfoContainer} from "./InfoContainer";
 import {InfoContext} from "./InfoContext";
 import {InfoMemory} from "./InfoMemory";
 import {KaravanApi} from "../../api/KaravanApi";
@@ -28,7 +28,7 @@ import {useProjectStore} from "../../api/ProjectStore";
 
 export const DashboardTab = () => {
 
-    const {project, podStatus} = useProjectStore();
+    const {project, containerStatus} = useProjectStore();
     const [memory, setMemory] = useState({});
     const [jvm, setJvm] = useState({});
     const [context, setContext] = useState({});
@@ -68,7 +68,7 @@ export const DashboardTab = () => {
     }
 
     function showConsole(): boolean {
-        return podStatus.ready;
+        return containerStatus.lifeCycle === 'ready';
     }
 
     return (
@@ -78,7 +78,7 @@ export const DashboardTab = () => {
                     <Flex direction={{default: "row"}}
                           justifyContent={{default: "justifyContentSpaceBetween"}}>
                         <FlexItem flex={{default: "flex_1"}}>
-                            <InfoPod podStatus={podStatus}/>
+                            <InfoContainer containerStatus={containerStatus}/>
                         </FlexItem>
                         <Divider orientation={{default: "vertical"}}/>
                         <FlexItem flex={{default: "flex_1"}}>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoPod.tsx b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx
similarity index 77%
rename from karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoPod.tsx
rename to karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx
index 5e544ac1..03bc5f9e 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoPod.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx
@@ -1,25 +1,22 @@
 import React from 'react';
 import {
-    Button,
     DescriptionList,
     DescriptionListDescription,
     DescriptionListGroup,
     DescriptionListTerm,
     Label,
-    Tooltip
 } from '@patternfly/react-core';
 import '../../designer/karavan.css';
 import DownIcon from "@patternfly/react-icons/dist/esm/icons/error-circle-o-icon";
 import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
-import {PodStatus} from "../../api/ProjectModels";
-import {useLogStore} from "../../api/ProjectStore";
+import {ContainerStatus} from "../../api/ProjectModels";
 
 
 interface Props {
-    podStatus: PodStatus,
+    containerStatus: ContainerStatus,
 }
 
-export const InfoPod = (props: Props) => {
+export const InfoContainer = (props: Props) => {
 
     function getPodInfoLabel(info: string) {
         return (
@@ -38,40 +35,40 @@ export const InfoPod = (props: Props) => {
     }
 
     function getRunning(): boolean {
-        return props.podStatus.ready;
+        return props.containerStatus.lifeCycle === 'ready';
     }
 
-    const podStatus = props.podStatus;
+    const containerStatus = props.containerStatus;
     return (
         <DescriptionList isHorizontal>
             <DescriptionListGroup>
                 <DescriptionListTerm>Pod</DescriptionListTerm>
                 <DescriptionListDescription>
-                    {getPodInfoLabel(podStatus.name)}
+                    {getPodInfoLabel(containerStatus.containerName)}
                 </DescriptionListDescription>
             </DescriptionListGroup>
             <DescriptionListGroup>
                 <DescriptionListTerm>Status</DescriptionListTerm>
                 <DescriptionListDescription>
-                    {getPodInfoLabel(podStatus.ready ? "Ready" : "Not Ready")}
+                    {getPodInfoLabel(containerStatus.lifeCycle)}
                 </DescriptionListDescription>
             </DescriptionListGroup>
             <DescriptionListGroup>
                 <DescriptionListTerm>CPU</DescriptionListTerm>
                 <DescriptionListDescription>
-                    {getPodInfoLabel(podStatus.cpuInfo)}
+                    {getPodInfoLabel(containerStatus.cpuInfo)}
                 </DescriptionListDescription>
             </DescriptionListGroup>
             <DescriptionListGroup>
                 <DescriptionListTerm>Memory</DescriptionListTerm>
                 <DescriptionListDescription>
-                    {getPodInfoLabel(podStatus.memoryInfo)}
+                    {getPodInfoLabel(containerStatus.memoryInfo)}
                 </DescriptionListDescription>
             </DescriptionListGroup>
             <DescriptionListGroup>
                 <DescriptionListTerm>Created</DescriptionListTerm>
                 <DescriptionListDescription>
-                    {getPodInfoLabel(podStatus.created)}
+                    {getPodInfoLabel(containerStatus.created)}
                 </DescriptionListDescription>
             </DescriptionListGroup>
         </DescriptionList>
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 29e2b8a4..491a2b80 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
@@ -14,7 +14,7 @@ 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 ClockIcon from "@patternfly/react-icons/dist/esm/icons/clock-icon";
 import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/times-circle-icon";
-import {CamelStatus, DeploymentStatus, PipelineStatus, PodStatus, Project} from "../../api/ProjectModels";
+import {CamelStatus, DeploymentStatus, PipelineStatus, ContainerStatus, Project} from "../../api/ProjectModels";
 import {useLogStore} from "../../api/ProjectStore";
 
 interface Props {
@@ -26,7 +26,7 @@ interface Props {
 interface State {
     pipelineStatus?: PipelineStatus,
     deploymentStatus?: DeploymentStatus,
-    podStatuses: PodStatus[],
+    podStatuses: ContainerStatus[],
     camelStatus?: CamelStatus,
     isPushing: boolean,
     isBuilding: boolean,
@@ -67,7 +67,7 @@ export class ProjectStatus extends React.Component<Props, State> {
                 this.setState({deploymentStatus: status});
                 // console.log(status);
             });
-            KaravanApi.getProjectPodStatuses(projectId, env, (statuses: PodStatus[]) => {
+            KaravanApi.getProjectPodStatuses(projectId, env, (statuses: ContainerStatus[]) => {
                 this.setState({podStatuses: statuses});
                 // console.log(status);
             });
@@ -193,7 +193,7 @@ export class ProjectStatus extends React.Component<Props, State> {
         )
     }
 
-    getPodsPanel(env: string, podStatuses: PodStatus[]) {
+    getPodsPanel(env: string, podStatuses: ContainerStatus[]) {
         return (
             <Flex justifyContent={{default: "justifyContentSpaceBetween"}}
                   alignItems={{default: "alignItemsFlexStart"}}>
@@ -201,27 +201,27 @@ export class ProjectStatus extends React.Component<Props, State> {
                     {podStatuses.length === 0 && <Label icon={<DownIcon/>} color={"grey"}>No pods</Label>}
                     <LabelGroup numLabels={2} isVertical>
                         {podStatuses.map(pod => {
-                                const ready = pod.ready;
+                                const ready = pod.lifeCycle === 'ready';
                                 return (
-                                    <Tooltip key={pod.name} content={ready ? "Ready" : "Not ready"}>
+                                    <Tooltip key={pod.containerName} content={pod.lifeCycle}>
                                         <Label icon={ready ? <UpIcon/> : <DownIcon/>} color={ready ? "green" : "red"}>
                                             <Button variant="link"
                                                     onClick={e => {
                                                         useLogStore.setState({
                                                             showLog: true,
                                                             type: 'container',
-                                                            podName: pod.name,
+                                                            podName: pod.containerName,
                                                             isRunning: true
                                                         });
                                                     }}>
-                                                {pod.name}
+                                                {pod.containerName}
                                             </Button>
                                             <Tooltip content={"Delete Pod"}>
                                                 <Button icon={<DeleteIcon/>} variant="link" onClick={e => this.setState({
                                                     showDeleteConfirmation: true,
                                                     deleteEntity: "pod",
                                                     deleteEntityEnv: env,
-                                                    deleteEntityName: pod.name
+                                                    deleteEntityName: pod.containerName
                                                 })}></Button>
                                             </Tooltip>
                                         </Label>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
index f10bf0ab..7363832a 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
@@ -57,6 +57,7 @@ export const TraceTab = () => {
         if (refreshTrace) {
             KaravanApi.getDevModeStatus(projectId, "trace", res => {
                 if (res.status === 200) {
+                    console.log(JSON.parse(res.data.status))
                     setTrace(JSON.parse(res.data.status));
                 } else {
                     setTrace({});
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 84621337..41aa5838 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
@@ -109,7 +109,7 @@ public class CamelService {
                     .filter(status -> status.getType().equals(ContainerStatus.CType.devmode) || status.getType().equals(ContainerStatus.CType.project))
                     .forEach(status -> {
                         CamelStatusRequest csr = new CamelStatusRequest(status.getProjectId(), status.getContainerName());
-                        eventBus.publish(CMD_COLLECT_CAMEL_STATUSES, JsonObject.mapFrom(csr));
+                        eventBus.publish(CMD_COLLECT_CAMEL_STATUS, JsonObject.mapFrom(csr));
                     });
         }
     }
diff --git a/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/EventService.java b/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/EventService.java
index d21cf33d..acc2513e 100644
--- a/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/EventService.java
+++ b/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/EventService.java
@@ -26,17 +26,11 @@ import javax.inject.Inject;
 @ApplicationScoped
 public class EventService {
 
-    public static final String CMD_COLLECT_CAMEL_STATUSES = "collect-camel-statuses";
     public static final String CMD_COLLECT_CAMEL_STATUS = "collect-camel-status";
 
     @Inject
     CamelService camelService;
 
-    @ConsumeEvent(value = CMD_COLLECT_CAMEL_STATUSES, blocking = true)
-    public void collectCamelStatuses(JsonObject data) {
-        camelService.collectCamelStatuses();
-    }
-
     @ConsumeEvent(value = CMD_COLLECT_CAMEL_STATUS, blocking = true)
     public void collectCamelStatus(JsonObject data) {
         camelService.collectCamelStatus(data);
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 7b0e92e2..9192e0ce 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
@@ -50,6 +50,8 @@ import static org.infinispan.query.remote.client.ProtobufMetadataManagerConstant
 @ApplicationScoped
 public class InfinispanService {
 
+    public static final String HEALTHY_STATUS = "healthy";
+
     @ConfigProperty(name ="infinispan.hosts")
     String infinispanHosts;
     @ConfigProperty(name ="infinispan.username")
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 b994df0d..66d983d8 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
@@ -92,9 +92,9 @@ public class ContainerStatus {
         return new ContainerStatus(projectId, projectId, null, null, null, env, CType.devmode, null, null, null,  Lifecycle.init, false, false);
     }
 
-    public static ContainerStatus createWithId(String name, String env, String containerId, List<Integer> ports, CType type, Lifecycle lifeCycle) {
+    public static ContainerStatus createWithId(String name, String env, String containerId, List<Integer> ports, CType type, Lifecycle lifeCycle, String created) {
         return new ContainerStatus(name, name, containerId, null, ports, env, type,
-                null, null, null,  lifeCycle, false, false);
+                null, null, created,  lifeCycle, false, false);
     }
 
     public ContainerStatus() {