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:31 UTC

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

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>