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/17 23:57:19 UTC

[camel-karavan] 02/02: Run/Reload/Stop in Docker #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 e507cb5ac45563d3eace11b98aebc27aa8166af0
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Mon Jul 17 19:57:07 2023 -0400

    Run/Reload/Stop in Docker #817
---
 .../apache/camel/karavan/api/DevModeResource.java  |  6 +-
 .../apache/camel/karavan/api/ProjectResource.java  | 22 ++++---
 .../camel/karavan/docker/DockerEventListener.java  | 23 ++++++-
 .../apache/camel/karavan/docker/DockerService.java | 37 +++++++----
 .../karavan/infinispan/InfinispanService.java      |  2 +-
 .../camel/karavan/infinispan/model/PodStatus.java  | 32 ++++++++--
 .../camel/karavan/kubernetes/PodEventHandler.java  |  3 +-
 .../apache/camel/karavan/service/CamelService.java | 73 +++++++++++++---------
 .../apache/camel/karavan/service/EventService.java |  6 +-
 .../apache/camel/karavan/service/GitService.java   | 18 +++---
 .../camel/karavan/service/ScheduledService.java    | 22 +++----
 .../org/apache/camel/karavan/shared/EventType.java |  9 +--
 .../src/main/resources/application.properties      | 19 +++---
 13 files changed, 169 insertions(+), 103 deletions(-)

diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java
index f703d5cf..3a00a428 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java
@@ -58,7 +58,7 @@ 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;
+        String runnerName = project.getProjectId() + DEVMODE_SUFFIX;
         PodStatus status = infinispanService.getDevModePodStatuses(runnerName, environment);
         if (status == null) {
             infinispanService.saveDevModeStatus(new DevModeStatus(project.getProjectId(), null, null, false));
@@ -92,7 +92,7 @@ public class DevModeResource {
     @Consumes(MediaType.APPLICATION_JSON)
     @Path("/{projectId}/{deletePVC}")
     public Response deleteRunner(@PathParam("projectId") String projectId, @PathParam("deletePVC") boolean deletePVC) {
-        String runnerName = projectId + "-" + DEVMODE_SUFFIX;
+        String runnerName = projectId + DEVMODE_SUFFIX;
         if (ConfigService.inKubernetes()) {
             kubernetesService.deleteRunner(runnerName, deletePVC);
         } else {
@@ -107,7 +107,7 @@ public class DevModeResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/pod/{projectId}")
     public Response getPodStatus(@PathParam("projectId") String projectId) throws RuntimeException {
-        String runnerName = projectId + "-" + DEVMODE_SUFFIX;
+        String runnerName = projectId + DEVMODE_SUFFIX;
         Optional<PodStatus> ps =  infinispanService.getPodStatuses(projectId, environment).stream()
                 .filter(podStatus -> podStatus.getName().equals(runnerName))
                 .findFirst();
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
index 343aeedb..8550441f 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
@@ -53,15 +53,19 @@ public class ProjectResource {
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     public List<Project> getAll() throws Exception {
-        return infinispanService.getProjects().stream()
-                .sorted((p1, p2) -> {
-                    if (p1.getProjectId().equalsIgnoreCase(Project.NAME_TEMPLATES)) return 1;
-                    if (p2.getProjectId().equalsIgnoreCase(Project.NAME_TEMPLATES)) return 1;
-                    if (p1.getProjectId().equalsIgnoreCase(Project.NAME_KAMELETS)) return 1;
-                    if (p2.getProjectId().equalsIgnoreCase(Project.NAME_KAMELETS)) return 1;
-                    return (p1.getProjectId().compareTo(p2.getProjectId()));
-                })
-                .collect(Collectors.toList());
+        if (infinispanService.isReady()) {
+            return infinispanService.getProjects().stream()
+                    .sorted((p1, p2) -> {
+                        if (p1.getProjectId().equalsIgnoreCase(Project.NAME_TEMPLATES)) return 1;
+                        if (p2.getProjectId().equalsIgnoreCase(Project.NAME_TEMPLATES)) return 1;
+                        if (p1.getProjectId().equalsIgnoreCase(Project.NAME_KAMELETS)) return 1;
+                        if (p2.getProjectId().equalsIgnoreCase(Project.NAME_KAMELETS)) return 1;
+                        return (p1.getProjectId().compareTo(p2.getProjectId()));
+                    })
+                    .collect(Collectors.toList());
+        } else {
+            return List.of();
+        }
     }
 
     @GET
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java
index cda60bf5..9db99b4c 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java
@@ -6,6 +6,7 @@ import com.github.dockerjava.api.model.ContainerPort;
 import com.github.dockerjava.api.model.Event;
 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;
@@ -23,6 +24,7 @@ import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
+import static org.apache.camel.karavan.shared.EventType.DEVMODE_STATUS;
 import static org.apache.camel.karavan.shared.EventType.INFINISPAN_STARTED;
 import static org.apache.camel.karavan.shared.ConfigService.DEVMODE_SUFFIX;
 
@@ -96,9 +98,7 @@ public class DockerEventListener implements ResultCallback<Event> {
                     infinispanService.deletePodStatus(projectId, environment, name);
                     infinispanService.deleteCamelStatuses(projectId, environment);
                 } else if (Arrays.asList("start", "unpause").contains(event.getStatus())) {
-                    String projectId = name.replace(DEVMODE_SUFFIX, "");
-                    PodStatus ps = new PodStatus(name, true, null, projectId, environment, true, Instant.ofEpochSecond(container.getCreated()).toString());
-                    infinispanService.savePodStatus(ps);
+                    savePodStatus(container);
                 } else if (status.startsWith("health_status:")) {
                     String health = status.replace("health_status: ", "");
                     LOGGER.infof("Container %s health status: %s", container.getNames()[0], health);
@@ -109,6 +109,7 @@ public class DockerEventListener implements ResultCallback<Event> {
                         dms.setContainerName(containerName);
                         dms.setContainerId(container.getId());
                         infinispanService.saveDevModeStatus(dms);
+                        eventBus.publish(DEVMODE_STATUS, JsonObject.mapFrom(dms));
                     }
                 }
             }
@@ -117,6 +118,22 @@ public class DockerEventListener implements ResultCallback<Event> {
         }
     }
 
+    protected void savePodStatus(Container container){
+        String name = container.getNames()[0].replace("/", "");
+        boolean inDevMode = name.endsWith(DEVMODE_SUFFIX);
+        String projectId = name.replace(DEVMODE_SUFFIX, "");
+        Integer exposedPort =  (container.getPorts().length > 0)  ? container.getPorts()[0].getPublicPort() : null;
+        if (infinispanService.isReady()) {
+            PodStatus ps = infinispanService.getDevModePodStatuses(projectId, environment);
+            if (ps == null) {
+                ps = new PodStatus(name, true, null, projectId, environment, inDevMode, Instant.ofEpochSecond(container.getCreated()).toString(), exposedPort);
+            } else {
+                ps.setExposedPort(exposedPort);
+            }
+            infinispanService.savePodStatus(ps);
+        }
+    }
+
     @Override
     public void onError(Throwable throwable) {
         LOGGER.error(throwable.getMessage());
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java
index 8b36c505..3fe2b106 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java
@@ -12,10 +12,11 @@ import com.github.dockerjava.core.DockerClientImpl;
 import com.github.dockerjava.core.InvocationBuilder;
 import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
 import com.github.dockerjava.transport.DockerHttpClient;
-import io.quarkus.scheduler.Scheduled;
 import io.smallrye.mutiny.tuples.Tuple2;
 import io.vertx.core.eventbus.EventBus;
 import io.vertx.core.json.JsonObject;
+import org.apache.camel.karavan.infinispan.InfinispanService;
+import org.apache.camel.karavan.infinispan.model.PodStatus;
 import org.apache.camel.karavan.infinispan.model.Project;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.jboss.logging.Logger;
@@ -24,13 +25,13 @@ import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import java.io.IOException;
 import java.text.DecimalFormat;
+import java.time.Instant;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
-import static org.apache.camel.karavan.shared.EventType.CONTAINER_STATS;
-import static org.apache.camel.karavan.shared.EventType.INFINISPAN_HEALTH;
 import static org.apache.camel.karavan.shared.ConfigService.DEVMODE_SUFFIX;
+import static org.apache.camel.karavan.shared.EventType.*;
 
 @ApplicationScoped
 public class DockerService {
@@ -67,7 +68,7 @@ public class DockerService {
         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, runnerImage,
-                List.of(), "", false, healthCheck,
+                List.of(), "8080:8080", true,true, healthCheck,
                 Map.of("type", "devmode", "projectId", projectId));
         startContainer(runnerName);
         LOGGER.infof("DevMode started for %s", projectId);
@@ -84,7 +85,7 @@ public class DockerService {
 
             createContainer(INFINISPAN_CONTAINER_NAME, infinispanImage,
                     List.of("USER=" + infinispanUsername, "PASS=" + infinispanPassword),
-                    infinispanPort, true, healthCheck, Map.of()
+                    infinispanPort, false, true, healthCheck, Map.of()
             );
             startContainer(INFINISPAN_CONTAINER_NAME);
             LOGGER.info("Infinispan is started");
@@ -110,6 +111,15 @@ public class DockerService {
         });
     }
 
+    public void collectContainersStatuses() {
+        getDockerClient().listContainersCmd().exec().forEach(container -> {
+            String name = container.getNames()[0].replace("/", "");
+            if (!Objects.equals(name, INFINISPAN_CONTAINER_NAME)) {
+                dockerEventListener.savePodStatus(container);
+            }
+        });
+    }
+
     public void startListeners() {
         getDockerClient().eventsCmd().exec(dockerEventListener);
     }
@@ -139,7 +149,7 @@ public class DockerService {
                 .forEach(c -> {
                     HealthState hs = getDockerClient().inspectContainerCmd(c.getId()).exec().getState().getHealth();
                     if (c.getNames()[0].equals("/" + INFINISPAN_CONTAINER_NAME)) {
-                        eventBus.publish(INFINISPAN_HEALTH, hs.getStatus());
+                        eventBus.publish(INFINISPAN_STARTED, hs.getStatus());
                     }
                 });
     }
@@ -167,7 +177,7 @@ public class DockerService {
         return stats;
     }
 
-    public Container createContainer(String name, String image, List<String> env, String ports,
+    public Container createContainer(String name, String image, List<String> env, String ports, boolean inRange,
                                      boolean exposedPort, HealthCheck healthCheck, Map<String, String> labels) throws InterruptedException {
         List<Container> containers = getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(name)).exec();
         if (containers.size() == 0) {
@@ -181,7 +191,7 @@ public class DockerService {
                     .withEnv(env)
                     .withExposedPorts(exposedPorts)
                     .withHostName(name)
-                    .withHostConfig(getHostConfig(ports, exposedPort))
+                    .withHostConfig(getHostConfig(ports, exposedPort, inRange))
                     .withHealthcheck(healthCheck)
                     .exec();
             LOGGER.info("Container created: " + response.getId());
@@ -254,13 +264,14 @@ public class DockerService {
         }
     }
 
-    private HostConfig getHostConfig(String ports, boolean exposedPort) {
+    private HostConfig getHostConfig(String ports, boolean exposedPort, boolean inRange) {
         Ports portBindings = new Ports();
+
         getPortsFromString(ports).forEach((hostPort, containerPort) -> {
-            portBindings.bind(
-                    ExposedPort.tcp(containerPort),
-                    exposedPort ? Ports.Binding.bindIp("0.0.0.0").bindPort(hostPort) : Ports.Binding.bindPort(hostPort)
-            );
+            Ports.Binding binding = exposedPort
+                    ? (inRange ? Ports.Binding.bindPortRange(hostPort, hostPort + 1000) : Ports.Binding.bindPort(hostPort))
+                    : Ports.Binding.bindPort(hostPort);
+            portBindings.bind(ExposedPort.tcp(containerPort), binding);
         });
         return new HostConfig()
                 .withPortBindings(portBindings)
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
index 24879223..fb200e3c 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
@@ -130,7 +130,7 @@ public class InfinispanService {
 
     public void saveProject(Project project) {
         GroupedKey key = GroupedKey.create(project.getProjectId(), DEFAULT_ENVIRONMENT, project.getProjectId());
-        boolean isNew = !projects.containsKey(key);
+        projects.put(key, project);
         projects.put(key, project);
     }
 
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/PodStatus.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/PodStatus.java
index f7aaa40e..47b5e0ff 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/PodStatus.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/PodStatus.java
@@ -16,15 +16,30 @@ public class PodStatus {
     @ProtoField(number = 5)
     String env;
     @ProtoField(number = 6)
-    Boolean inDevMode;
+    Integer exposedPort;
     @ProtoField(number = 7)
-    String memoryInfo;
+    Boolean inDevMode;
     @ProtoField(number = 8)
-    String cpuInfo;
+    String memoryInfo;
     @ProtoField(number = 9)
+    String cpuInfo;
+    @ProtoField(number = 10)
     String created;
 
     @ProtoFactory
+    public PodStatus(String name, Boolean ready, String deployment, String projectId, String env, Integer exposedPort, Boolean inDevMode, String memoryInfo, String cpuInfo, String created) {
+        this.name = name;
+        this.ready = ready;
+        this.deployment = deployment;
+        this.projectId = projectId;
+        this.env = env;
+        this.exposedPort = exposedPort;
+        this.inDevMode = inDevMode;
+        this.memoryInfo = memoryInfo;
+        this.cpuInfo = cpuInfo;
+        this.created = created;
+    }
+
     public PodStatus(String name, Boolean ready, String deployment, String projectId, String env, Boolean inDevMode, String memoryInfo, String cpuInfo, String created) {
         this.name = name;
         this.ready = ready;
@@ -37,7 +52,7 @@ public class PodStatus {
         this.created = created;
     }
 
-    public PodStatus(String name, Boolean ready, String deployment, String projectId, String env, Boolean inDevMode, String created) {
+    public PodStatus(String name, Boolean ready, String deployment, String projectId, String env, Boolean inDevMode, String created, Integer exposedPort) {
         this.name = name;
         this.ready = ready;
         this.deployment = deployment;
@@ -45,6 +60,7 @@ public class PodStatus {
         this.env = env;
         this.inDevMode = inDevMode;
         this.created = created;
+        this.exposedPort = exposedPort;
     }
 
     public String getName() {
@@ -118,4 +134,12 @@ public class PodStatus {
     public void setCreated(String created) {
         this.created = created;
     }
+
+    public Integer getExposedPort() {
+        return exposedPort;
+    }
+
+    public void setExposedPort(Integer exposedPort) {
+        this.exposedPort = exposedPort;
+    }
 }
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java
index 3c0e6f84..66eb1e54 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java
@@ -94,7 +94,8 @@ public class PodEventHandler implements ResourceEventHandler<Pod> {
                     project,
                     kubernetesService.environment,
                     false,
-                    "");
+                    "",
+                    null);
         }
     }
 }
\ No newline at end of file
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java
index 70d988d6..67077914 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java
@@ -75,10 +75,12 @@ public class CamelService {
 
     public void reloadProjectCode(String projectId) {
         LOGGER.info("Reload project code " + projectId);
-        String containerName = projectId + "-" + DEVMODE_SUFFIX;
+        String containerName = projectId + DEVMODE_SUFFIX;
         try {
-            infinispanService.getProjectFiles(projectId).forEach(projectFile -> putRequest(containerName, projectFile.getName(), projectFile.getCode(), 1000));
-            reloadRequest(containerName);
+            PodStatus podStatus = infinispanService.getDevModePodStatuses(projectId, environment);
+            Integer exposedPort = podStatus.getExposedPort();
+            infinispanService.getProjectFiles(projectId).forEach(projectFile -> putRequest(containerName,exposedPort, projectFile.getName(), projectFile.getCode(), 1000));
+            reloadRequest(containerName, exposedPort);
             DevModeStatus dms = infinispanService.getDevModeStatus(projectId);
             dms.setCodeLoaded(true);
             infinispanService.saveDevModeStatus(dms);
@@ -88,9 +90,9 @@ public class CamelService {
     }
 
     @CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay = 1000)
-    public boolean putRequest(String containerName, String fileName, String body, int timeout) {
+    public boolean putRequest(String containerName, Integer exposedPort, String fileName, String body, int timeout) {
         try {
-            String url = getContainerAddress(containerName) + "/q/upload/" + fileName;
+            String url = getContainerAddress(containerName, exposedPort) + "/q/upload/" + fileName;
             HttpResponse<Buffer> result = getWebClient().putAbs(url)
                     .timeout(timeout).sendBuffer(Buffer.buffer(body)).subscribeAsCompletionStage().toCompletableFuture().get();
             return result.statusCode() == 200;
@@ -100,8 +102,8 @@ public class CamelService {
         return false;
     }
 
-    public String reloadRequest(String containerName) {
-        String url = getContainerAddress(containerName) + "/q/dev/reload?reload=true";
+    public String reloadRequest(String containerName, Integer exposedPort) {
+        String url = getContainerAddress(containerName, exposedPort) + "/q/dev/reload?reload=true";
         try {
             return result(url, 1000);
         } catch (InterruptedException | ExecutionException e) {
@@ -110,27 +112,18 @@ public class CamelService {
         return null;
     }
 
-    public String getContainerAddress(String containerName) {
+    public String getContainerAddress(String containerName, Integer exposedPort) {
         if (ConfigService.inKubernetes()) {
             return "http://" + containerName + "." + kubernetesService.getNamespace() + ".svc.cluster.local";
         } else {
-            return "http://" + containerName + ":8080";
+            return "http://localhost:" + exposedPort;
         }
     }
 
-    public void collectDevModeStatuses() {
-        if (infinispanService.isReady()) {
-            infinispanService.getDevModeStatuses().forEach(dms -> {
-                CamelStatusRequest csr = new CamelStatusRequest(dms.getProjectId(), dms.getContainerName());
-                eventBus.publish(CMD_COLLECT_CAMEL_STATUS, JsonObject.mapFrom(csr));
-            });
-        }
-    }
-
-    public void collectNonDevModeStatuses() {
+    public void collectCamelStatuses() {
         if (infinispanService.isReady()) {
             infinispanService.getPodStatuses(environment).forEach(pod -> {
-                CamelStatusRequest csr = new CamelStatusRequest(pod.getProjectId(), pod.getName());
+                CamelStatusRequest csr = new CamelStatusRequest(pod.getProjectId(), pod.getName(), pod.getExposedPort());
                 eventBus.publish(CMD_COLLECT_CAMEL_STATUS, JsonObject.mapFrom(csr));
             });
         }
@@ -138,10 +131,11 @@ public class CamelService {
 
     @ConsumeEvent(value = CMD_COLLECT_CAMEL_STATUS, blocking = true, ordered = true)
     public void collectCamelStatuses(JsonObject data) {
-        DevModeStatus dms = data.mapTo(DevModeStatus.class);
+        CamelStatusRequest dms = data.mapTo(CamelStatusRequest.class);
         Arrays.stream(CamelStatusName.values()).forEach(statusName -> {
             String containerName = dms.getContainerName();
-            String status = getCamelStatus(containerName, statusName);
+            Integer exposedPort = dms.getExposedPort();
+            String status = getCamelStatus(containerName, exposedPort, statusName);
             if (status != null) {
                 CamelStatus cs = new CamelStatus(dms.getProjectId(), containerName, statusName, status, environment);
                 infinispanService.saveCamelStatus(cs);
@@ -169,7 +163,7 @@ public class CamelService {
     }
 
     private void reloadCode(String podName, String oldContext, String newContext) {
-        String projectName = podName.replace("-" + DEVMODE_SUFFIX, "");
+        String projectName = podName.replace(DEVMODE_SUFFIX, "");
         String newState = getContextState(newContext);
         String oldState = getContextState(oldContext);
         if (newContext != null && !Objects.equals(newState, oldState) && "Started".equals(newState)) {
@@ -186,8 +180,8 @@ public class CamelService {
         }
     }
 
-    public String getCamelStatus(String podName, CamelStatusName statusName) {
-        String url = getContainerAddress(podName) + "/q/dev/" + statusName.name();
+    public String getCamelStatus(String podName, Integer exposedPort, CamelStatusName statusName) {
+        String url = getContainerAddress(podName, exposedPort) + "/q/dev/" + statusName.name();
         try {
             return result(url, 500);
         } catch (InterruptedException | ExecutionException e) {
@@ -211,21 +205,42 @@ public class CamelService {
         return null;
     }
 
-    public class CamelStatusRequest {
-        private final String projectId;
-        private final String containerName;
+    public static class CamelStatusRequest {
+        private String projectId;
+        private String containerName;
+        private Integer exposedPort;
+
+        public CamelStatusRequest() {
+        }
 
-        public CamelStatusRequest(String projectId, String containerName) {
+        public CamelStatusRequest(String projectId, String containerName, Integer exposedPort) {
             this.projectId = projectId;
             this.containerName = containerName;
+            this.exposedPort = exposedPort;
         }
 
         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 Integer getExposedPort() {
+            return exposedPort;
+        }
+
+        public void setExposedPort(Integer exposedPort) {
+            this.exposedPort = exposedPort;
+        }
     }
 }
\ No newline at end of file
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java
index 64ef9255..02e4bf45 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java
@@ -50,9 +50,9 @@ public class EventService {
         if (!ConfigService.inKubernetes()) {
             LOGGER.info("Starting Karavan with Docker");
             dockerService.createNetwork();
-            dockerService.checkInfinispanHealth();
             dockerService.startListeners();
             dockerService.startInfinispan();
+            dockerService.checkInfinispanHealth();
         } else {
             LOGGER.info("Starting Karavan in " + (kubernetesService.isOpenshift() ? "OpenShift" : "Kubernetes"));
             startServices(HEALTHY);
@@ -75,7 +75,7 @@ public class EventService {
         if (ConfigService.inKubernetes()) {
             kubernetesService.startInformers(data);
         } else {
-            dockerService.startListeners();
+//            Docker listener is already started
         }
     }
 
@@ -94,7 +94,7 @@ public class EventService {
         projectService.importProjects(data);
     }
 
-    @ConsumeEvent(value = ADDRESS_DEVMODE_STATUS, blocking = true, ordered = true)
+    @ConsumeEvent(value = DEVMODE_STATUS, blocking = true, ordered = true)
     void receiveCommand(JsonObject message) {
         LOGGER.info("received Status " + message);
         DevModeStatus status = message.mapTo(DevModeStatus.class);
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java
index 90c8b61b..c1e1e385 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java
@@ -132,21 +132,21 @@ public class GitService {
 
     public GitConfig getGitConfig() {
         String propertiesPrefix = "karavan.";
-        String branch = ConfigProvider.getConfig().getValue(propertiesPrefix + "git-branch", String.class);
+        String branch = ConfigProvider.getConfig().getValue(propertiesPrefix + "git.branch", String.class);
         if (ConfigService.inKubernetes()) {
             LOGGER.info("inKubernetes " + kubernetesService.getNamespace());
             Secret secret = kubernetesService.getKaravanSecret();
-            String uri = new String(Base64.getDecoder().decode(secret.getData().get("git-repository").getBytes(StandardCharsets.UTF_8)));
-            String username = new String(Base64.getDecoder().decode(secret.getData().get("git-username").getBytes(StandardCharsets.UTF_8)));
-            String password = new String(Base64.getDecoder().decode(secret.getData().get("git-password").getBytes(StandardCharsets.UTF_8)));
-            if (secret.getData().containsKey("git-branch")) {
-                branch = new String(Base64.getDecoder().decode(secret.getData().get("git-branch").getBytes(StandardCharsets.UTF_8)));
+            String uri = new String(Base64.getDecoder().decode(secret.getData().get("git.repository").getBytes(StandardCharsets.UTF_8)));
+            String username = new String(Base64.getDecoder().decode(secret.getData().get("git.username").getBytes(StandardCharsets.UTF_8)));
+            String password = new String(Base64.getDecoder().decode(secret.getData().get("git.password").getBytes(StandardCharsets.UTF_8)));
+            if (secret.getData().containsKey("git.branch")) {
+                branch = new String(Base64.getDecoder().decode(secret.getData().get("git.branch").getBytes(StandardCharsets.UTF_8)));
             }
             return new GitConfig(uri, username, password, branch);
         } else {
-            String uri = ConfigProvider.getConfig().getValue(propertiesPrefix + "git-repository", String.class);
-            String username = ConfigProvider.getConfig().getValue(propertiesPrefix + "git-username", String.class);
-            String password = ConfigProvider.getConfig().getValue(propertiesPrefix + "git-password", String.class);
+            String uri = ConfigProvider.getConfig().getValue(propertiesPrefix + "git.repository", String.class);
+            String username = ConfigProvider.getConfig().getValue(propertiesPrefix + "git.username", String.class);
+            String password = ConfigProvider.getConfig().getValue(propertiesPrefix + "git.password", String.class);
             return new GitConfig(uri, username, password, branch);
         }
     }
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java
index 4a1103b4..953b660d 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java
@@ -34,27 +34,27 @@ public class ScheduledService {
     @Inject
     CamelService camelService;
 
-    @Scheduled(every = "{karavan.container-stats-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
-    void collectContainersStats() {
-        dockerService.collectContainersStats();
+    @Scheduled(every = "{karavan.container.status.pull-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
+    void collectContainersStatuses() {
+        dockerService.collectContainersStatuses();
     }
 
-    @Scheduled(every = "{karavan.devmode-status-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
-    void collectDevModeCamelStatuses() {
-        camelService.collectDevModeStatuses();
+    @Scheduled(every = "{karavan.container.stats.container}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
+    void collectContainersStats() {
+        dockerService.collectContainersStats();
     }
 
-    @Scheduled(every = "{karavan.camel-status-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
-    void collectNonDevModeCamelStatuses() {
-        camelService.collectNonDevModeStatuses();
+    @Scheduled(every = "{karavan.camel.status.pull-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
+    void collectCamelStatuses() {
+        camelService.collectCamelStatuses();
     }
 
-    @Scheduled(every = "{karavan.devmode-status-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
+    @Scheduled(every = "{karavan.camel.status.pull-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
     void cleanupDevModeStatuses() {
         camelService.cleanupDevModeStatuses();
     }
 
-    @Scheduled(every = "{karavan.git-pull-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
+    @Scheduled(every = "{karavan.git.pull-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
     void pullCommitsFromGit() {
         projectService.pullCommits();
     }
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java
index 7b7af56e..f2036913 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java
@@ -11,14 +11,9 @@ public class EventType {
     //    Import projects from Git repository
     public static final String IMPORT_PROJECTS = "IMPORT_PROJECTS";
 
-
-
     public static final String INFINISPAN_STARTED = "INFINISPAN_STARTED";
-    public static final String INFINISPAN_HEALTH = "INFINISPAN_HEALTH";
 
-    public static final String CONTAINER_STATS = "ADDRESS_CONTAINER_STATS";
+    public static final String CONTAINER_STATS = "CONTAINER_STATS";
+    public static final String DEVMODE_STATUS = "DEVMODE_STATUS";
 
-    public static final String ADDRESS_DEVMODE_STATUS = "ADDRESS_DEVMODE_STATUS";
-    public static final String ADDRESS_DEVMODE_COMMAND_INTERNAL = "ADDRESS_DEVMODE_COMMAND_INTERNAL";
-    public static final String ADDRESS_DEVMODE_STATUS_INTERNAL = "ADDRESS_DEVMODE_STATUS_INTERNAL";
 }
diff --git a/karavan-cloud/karavan-app/src/main/resources/application.properties b/karavan-cloud/karavan-app/src/main/resources/application.properties
index b153e02b..0cb2e699 100644
--- a/karavan-cloud/karavan-app/src/main/resources/application.properties
+++ b/karavan-cloud/karavan-app/src/main/resources/application.properties
@@ -3,19 +3,18 @@ karavan.environment=dev
 karavan.environments=dev,test,prod
 karavan.default-runtime=quarkus
 karavan.runtimes=quarkus,spring-boot
-karavan.camel-status-interval=10s
-karavan.devmode-status-interval=2s
-karavan.container-status-interval=10s
-karavan.container-stats-interval=10s
+karavan.camel.status.pull-interval=3s
+karavan.container.status.pull-interval=5s
+karavan.container.stats.container=5s
 quarkus.scheduler.enabled=true
-runner.image=marat/karavan
+runner.image=ghcr.io/apache/camel-karavan-runner:3.21.1-snapshot
 
 # Git repository Configuration
-karavan.git-repository=${GIT_REPOSITORY}
-karavan.git-username=${GIT_USERNAME}
-karavan.git-password=${GIT_TOKEN}
-karavan.git-branch=main
-karavan.git-pull-interval=disabled
+karavan.git.repository=${GIT_REPOSITORY}
+karavan.git.username=${GIT_USERNAME}
+karavan.git.password=${GIT_TOKEN}
+karavan.git.branch=main
+karavan.git.pull-interval=disabled
 
 # Infinispan container config in Docker
 infinispan.image=quay.io/infinispan/server:14.0.6.Final