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

[camel-karavan] 02/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 a26b5660eacddfd4bd2b547f7bfad957b437b70d
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Sat Jul 22 20:54:17 2023 -0400

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

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