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 3d937a8b731869199b47259a369840d1656cc6de
Author: Marat Gubaidullin <[email protected]>
AuthorDate: Fri Sep 23 17:52:33 2022 -0400

    Pipeline and Deployment status cleanup
---
 .../apache/camel/karavan/api/StatusResource.java   |  82 ++++++-----
 .../camel/karavan/model/DeploymentStatus.java      |  25 +++-
 .../apache/camel/karavan/model/PipelineStatus.java |  74 ++++++++++
 .../camel/karavan/model/ProjectEnvStatus.java      |  14 +-
 .../apache/camel/karavan/model/ProjectStatus.java  |  60 --------
 .../camel/karavan/model/ProjectStoreSchema.java    |   4 +-
 .../camel/karavan/service/InfinispanService.java   |  34 +++--
 .../camel/karavan/service/KaravanService.java      |   5 +
 .../camel/karavan/service/KubernetesService.java   | 117 ++++++++++-----
 .../camel/karavan/service/StatusService.java       | 158 +++++++++------------
 .../camel/karavan/watcher/DeploymentWatcher.java   |  59 ++++++++
 .../camel/karavan/watcher/PipelineRunWatcher.java  |  74 ++++++++++
 .../apache/camel/karavan/watcher/PodWatcher.java   |  40 ++++++
 .../src/main/resources/application.properties      |  14 +-
 karavan-app/src/main/webapp/src/api/KaravanApi.tsx |  17 ++-
 .../main/webapp/src/projects/ProjectDashboard.tsx  | 100 ++++++-------
 .../src/main/webapp/src/projects/ProjectInfo.tsx   |  52 ++++---
 .../src/main/webapp/src/projects/ProjectModels.ts  |  34 +++--
 .../src/main/webapp/src/projects/ProjectPage.tsx   |   3 +-
 .../src/main/webapp/src/projects/ProjectsPage.tsx  |   4 +-
 20 files changed, 621 insertions(+), 349 deletions(-)

diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java 
b/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java
index b33cac1..9492cc2 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java
@@ -17,11 +17,10 @@
 package org.apache.camel.karavan.api;
 
 import io.vertx.core.eventbus.EventBus;
+import org.apache.camel.karavan.model.DeploymentStatus;
 import org.apache.camel.karavan.model.KaravanConfiguration;
-import org.apache.camel.karavan.model.ProjectEnvStatus;
-import org.apache.camel.karavan.model.ProjectStatus;
+import org.apache.camel.karavan.model.PipelineStatus;
 import org.apache.camel.karavan.service.InfinispanService;
-import org.apache.camel.karavan.service.StatusService;
 import org.jboss.logging.Logger;
 
 import javax.inject.Inject;
@@ -31,9 +30,7 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.stream.Collectors;
+import javax.ws.rs.core.Response;
 
 @Path("/api/status")
 public class StatusResource {
@@ -51,44 +48,51 @@ public class StatusResource {
 
     @GET
     @Produces(MediaType.APPLICATION_JSON)
-    @Path("/project/{projectId}")
-    public ProjectStatus getStatus(@HeaderParam("username") String username, 
@PathParam("projectId") String projectId) {
-        bus.publish(StatusService.CMD_COLLECT_STATUSES, projectId);
-        ProjectStatus status = infinispanService.getProjectStatus(projectId);
-        if (status != null){
-            return status;
+    @Path("/pipeline/{projectId}")
+    public Response getPipelineStatus(@HeaderParam("username") String 
username, @PathParam("projectId") String projectId) {
+        PipelineStatus status = infinispanService.getPipelineStatus(projectId);
+        if (status != null) {
+            return Response.ok(status).build();
         } else {
-            return new ProjectStatus( projectId,
-                    configuration.environments()
-                            .stream().map(e -> new ProjectEnvStatus(e.name()))
-                            .collect(Collectors.toList()),
-                    Long.valueOf(0));
+            return Response.noContent().build();
         }
     }
 
     @GET
     @Produces(MediaType.APPLICATION_JSON)
-    @Path("/projects")
-    public Map<String, Map> getSimpleStatus(@HeaderParam("username") String 
username) throws Exception {
-        Map<String, Map> result = new HashMap<>();
-        infinispanService.getProjects().forEach(project -> {
-            ProjectStatus ps = getStatus(username, project.getProjectId());
-            Map<String, String> statuses = new HashMap<>();
-            ps.getStatuses().forEach(pes -> {
-                if (pes.getLastPipelineRunResult() == null || 
pes.getDeploymentStatus() == null || pes.getContextStatus() == null){
-                    statuses.put(pes.getEnvironment(), "N/A");
-                } else {
-                    boolean pipelineOK = 
pes.getLastPipelineRunResult().equals("Succeeded");
-                    System.out.println(pes.getLastPipelineRunResult());
-                    boolean deploymentOK = 
pes.getDeploymentStatus().getReadyReplicas() == 
pes.getDeploymentStatus().getReplicas() && 
pes.getDeploymentStatus().getUnavailableReplicas() == 0;
-                    boolean camelOK = 
pes.getContextStatus().equals(ProjectEnvStatus.Status.UP) && 
pes.getConsumerStatus().equals(ProjectEnvStatus.Status.UP) && 
pes.getRoutesStatus().equals(ProjectEnvStatus.Status.UP);
-                    String status = (pipelineOK && deploymentOK && camelOK) ? 
"UP" : "DOWN";
-                    statuses.put(pes.getEnvironment(), status);
-                }
-            });
-            result.put(project.getProjectId(), statuses);
-        });
-
-        return result;
+    @Path("/deployment/{projectId}")
+    public Response getDeploymentStatus(@HeaderParam("username") String 
username, @PathParam("projectId") String projectId) {
+        DeploymentStatus status = 
infinispanService.getDeploymentStatus(projectId);
+        if (status != null) {
+            return Response.ok(status).build();
+        } else {
+            return Response.noContent().build();
+        }
     }
+
+//    @GET
+//    @Produces(MediaType.APPLICATION_JSON)
+//    @Path("/projects")
+//    public Map<String, Map> getSimpleStatus(@HeaderParam("username") String 
username) throws Exception {
+//        Map<String, Map> result = new HashMap<>();
+//        infinispanService.getProjects().forEach(project -> {
+//            ProjectStatus ps = getStatus(username, project.getProjectId());
+//            Map<String, String> statuses = new HashMap<>();
+//            ps.getStatuses().forEach(pes -> {
+//                if (pes.getLastPipelineRunResult() == null || 
pes.getDeploymentStatus() == null || pes.getContextStatus() == null){
+//                    statuses.put(pes.getEnvironment(), "N/A");
+//                } else {
+//                    boolean pipelineOK = 
pes.getLastPipelineRunResult().equals("Succeeded");
+//                    System.out.println(pes.getLastPipelineRunResult());
+//                    boolean deploymentOK = 
pes.getDeploymentStatus().getReadyReplicas() == 
pes.getDeploymentStatus().getReplicas() && 
pes.getDeploymentStatus().getUnavailableReplicas() == 0;
+//                    boolean camelOK = 
pes.getContextStatus().equals(ProjectEnvStatus.Status.UP) && 
pes.getConsumerStatus().equals(ProjectEnvStatus.Status.UP) && 
pes.getRoutesStatus().equals(ProjectEnvStatus.Status.UP);
+//                    String status = (pipelineOK && deploymentOK && camelOK) 
? "UP" : "DOWN";
+//                    statuses.put(pes.getEnvironment(), status);
+//                }
+//            });
+//            result.put(project.getProjectId(), statuses);
+//        });
+//
+//        return result;
+//    }
 }
\ No newline at end of file
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/model/DeploymentStatus.java
 
b/karavan-app/src/main/java/org/apache/camel/karavan/model/DeploymentStatus.java
index 445e5a9..732bdb1 100644
--- 
a/karavan-app/src/main/java/org/apache/camel/karavan/model/DeploymentStatus.java
+++ 
b/karavan-app/src/main/java/org/apache/camel/karavan/model/DeploymentStatus.java
@@ -7,19 +7,23 @@ import java.util.ArrayList;
 import java.util.List;
 
 public class DeploymentStatus {
+    public static final String CACHE = "deployment_statuses";
     @ProtoField(number = 1)
-    String image;
+    String projectId;
     @ProtoField(number = 2)
-    Integer replicas;
+    String image;
     @ProtoField(number = 3)
-    Integer readyReplicas;
+    Integer replicas;
     @ProtoField(number = 4)
+    Integer readyReplicas;
+    @ProtoField(number = 5)
     Integer unavailableReplicas;
-    @ProtoField(number = 5, collectionImplementation = ArrayList.class)
+    @ProtoField(number = 6, collectionImplementation = ArrayList.class)
     List<PodStatus> podStatuses;
 
 
-    public DeploymentStatus() {
+    public DeploymentStatus(String projectId) {
+        this.projectId = projectId;
         this.image = "";
         this.replicas = 0;
         this.readyReplicas = 0;
@@ -28,7 +32,8 @@ public class DeploymentStatus {
     }
 
     @ProtoFactory
-    public DeploymentStatus(String image, Integer replicas, Integer 
readyReplicas, Integer unavailableReplicas, List<PodStatus> podStatuses) {
+    public DeploymentStatus(String projectId, String image, Integer replicas, 
Integer readyReplicas, Integer unavailableReplicas, List<PodStatus> 
podStatuses) {
+        this.projectId = projectId;
         this.image = image;
         this.replicas = replicas;
         this.readyReplicas = readyReplicas;
@@ -36,6 +41,14 @@ public class DeploymentStatus {
         this.podStatuses = podStatuses;
     }
 
+    public String getProjectId() {
+        return projectId;
+    }
+
+    public void setProjectId(String projectId) {
+        this.projectId = projectId;
+    }
+
     public String getImage() {
         return image;
     }
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/model/PipelineStatus.java 
b/karavan-app/src/main/java/org/apache/camel/karavan/model/PipelineStatus.java
new file mode 100644
index 0000000..8f6f807
--- /dev/null
+++ 
b/karavan-app/src/main/java/org/apache/camel/karavan/model/PipelineStatus.java
@@ -0,0 +1,74 @@
+package org.apache.camel.karavan.model;
+
+import org.infinispan.protostream.annotations.ProtoFactory;
+import org.infinispan.protostream.annotations.ProtoField;
+
+public class PipelineStatus {
+    public static final String CACHE = "pipeline_statuses";
+    @ProtoField(number = 1)
+    String projectId;
+    @ProtoField(number = 2)
+    String pipelineName;
+    @ProtoField(number = 3)
+    String result;
+    @ProtoField(number = 5)
+    String startTime;
+    @ProtoField(number = 6)
+    String completionTime;
+
+    @ProtoFactory
+    public PipelineStatus(String projectId, String pipelineName, String 
result, String startTime, String completionTime) {
+        this.projectId = projectId;
+        this.pipelineName = pipelineName;
+        this.result = result;
+        this.startTime = startTime;
+        this.completionTime = completionTime;
+    }
+
+    public PipelineStatus(String projectId) {
+        this.projectId = projectId;
+    }
+
+    public PipelineStatus() {
+    }
+
+    public String getProjectId() {
+        return projectId;
+    }
+
+    public void setProjectId(String projectId) {
+        this.projectId = projectId;
+    }
+
+    public String getPipelineName() {
+        return pipelineName;
+    }
+
+    public void setPipelineName(String pipelineName) {
+        this.pipelineName = pipelineName;
+    }
+
+    public String getResult() {
+        return result;
+    }
+
+    public void setResult(String result) {
+        this.result = result;
+    }
+
+    public String getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(String startTime) {
+        this.startTime = startTime;
+    }
+
+    public String getCompletionTime() {
+        return completionTime;
+    }
+
+    public void setCompletionTime(String completionTime) {
+        this.completionTime = completionTime;
+    }
+}
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectEnvStatus.java
 
b/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectEnvStatus.java
index ff83922..99192a6 100644
--- 
a/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectEnvStatus.java
+++ 
b/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectEnvStatus.java
@@ -27,6 +27,8 @@ public class ProjectEnvStatus {
     Long lastPipelineRunTime;
     @ProtoField(number = 11)
     DeploymentStatus deploymentStatus;
+    @ProtoField(number = 12)
+    String lastPipelineRunStartTime;
 
     public enum Status {
         @ProtoEnumValue(number = 0, name = "DOWN")
@@ -38,7 +40,8 @@ public class ProjectEnvStatus {
     }
 
     @ProtoFactory
-    public ProjectEnvStatus(String environment, Status status, Status 
contextStatus, Status consumerStatus, Status routesStatus, Status 
registryStatus, String contextVersion, String lastPipelineRun, String 
lastPipelineRunResult, Long lastPipelineRunTime, DeploymentStatus 
deploymentStatus) {
+    public ProjectEnvStatus(String environment, Status status, Status 
contextStatus, Status consumerStatus, Status routesStatus, Status 
registryStatus, String contextVersion,
+                            String lastPipelineRun, String 
lastPipelineRunResult, Long lastPipelineRunTime, DeploymentStatus 
deploymentStatus, String lastPipelineRunStartTime) {
         this.environment = environment;
         this.status = status;
         this.contextStatus = contextStatus;
@@ -50,6 +53,7 @@ public class ProjectEnvStatus {
         this.lastPipelineRunResult = lastPipelineRunResult;
         this.lastPipelineRunTime = lastPipelineRunTime;
         this.deploymentStatus = deploymentStatus;
+        this.lastPipelineRunStartTime = lastPipelineRunStartTime;
     }
 
     public ProjectEnvStatus(String environment) {
@@ -143,4 +147,12 @@ public class ProjectEnvStatus {
     public void setLastPipelineRunTime(Long lastPipelineRunTime) {
         this.lastPipelineRunTime = lastPipelineRunTime;
     }
+
+    public String getLastPipelineRunStartTime() {
+        return lastPipelineRunStartTime;
+    }
+
+    public void setLastPipelineRunStartTime(String lastPipelineRunStartTime) {
+        this.lastPipelineRunStartTime = lastPipelineRunStartTime;
+    }
 }
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectStatus.java 
b/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectStatus.java
deleted file mode 100644
index e7145ea..0000000
--- 
a/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectStatus.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package org.apache.camel.karavan.model;
-
-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 ProjectStatus {
-    public static final String CACHE = "project_statuses";
-    @ProtoField(number = 1)
-    String projectId;
-    @ProtoField(number = 2, collectionImplementation = ArrayList.class)
-    List<ProjectEnvStatus> statuses;
-    @ProtoField(number = 3)
-    Long lastUpdate;
-
-    public enum Status {
-        @ProtoEnumValue(number = 0, name = "DOWN")
-        DOWN,
-        @ProtoEnumValue(number = 1, name = "UP")
-        UP
-    }
-
-    @ProtoFactory
-    public ProjectStatus(String projectId, List<ProjectEnvStatus> statuses, 
Long lastUpdate) {
-        this.projectId = projectId;
-        this.statuses = statuses;
-        this.lastUpdate = lastUpdate;
-    }
-
-    public ProjectStatus() {
-    }
-
-    public String getProjectId() {
-        return projectId;
-    }
-
-    public void setProjectId(String projectId) {
-        this.projectId = projectId;
-    }
-
-    public List<ProjectEnvStatus> getStatuses() {
-        return statuses;
-    }
-
-    public void setStatuses(List<ProjectEnvStatus> statuses) {
-        this.statuses = statuses;
-    }
-
-    public Long getLastUpdate() {
-        return lastUpdate;
-    }
-
-    public void setLastUpdate(Long lastUpdate) {
-        this.lastUpdate = lastUpdate;
-    }
-
-}
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectStoreSchema.java
 
b/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectStoreSchema.java
index a049831..4721887 100644
--- 
a/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectStoreSchema.java
+++ 
b/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectStoreSchema.java
@@ -3,11 +3,9 @@ package org.apache.camel.karavan.model;
 import org.infinispan.protostream.GeneratedSchema;
 import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;
 
-import java.util.HashMap;
-
 @AutoProtoSchemaBuilder(
         includeClasses = {
-                GroupedKey.class, Project.class, ProjectFile.class, 
ProjectStatus.class, ProjectEnvStatus.class, DeploymentStatus.class,
+                GroupedKey.class, Project.class, ProjectFile.class, 
PipelineStatus.class, ProjectEnvStatus.class, DeploymentStatus.class,
                 PodStatus.class
         },
         schemaPackageName = "karavan")
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java
 
b/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java
index e99a406..189bed0 100644
--- 
a/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java
+++ 
b/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java
@@ -16,14 +16,14 @@
  */
 package org.apache.camel.karavan.service;
 
-import io.quarkus.runtime.StartupEvent;
 import io.quarkus.runtime.configuration.ProfileManager;
 import io.vertx.core.eventbus.EventBus;
+import org.apache.camel.karavan.model.DeploymentStatus;
 import org.apache.camel.karavan.model.GroupedKey;
 import org.apache.camel.karavan.model.Kamelet;
+import org.apache.camel.karavan.model.PipelineStatus;
 import org.apache.camel.karavan.model.Project;
 import org.apache.camel.karavan.model.ProjectFile;
-import org.apache.camel.karavan.model.ProjectStatus;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.infinispan.Cache;
 import org.infinispan.client.hotrod.RemoteCache;
@@ -35,14 +35,12 @@ import 
org.infinispan.commons.configuration.XMLStringConfiguration;
 import org.infinispan.configuration.cache.CacheMode;
 import org.infinispan.configuration.cache.ConfigurationBuilder;
 import org.infinispan.configuration.cache.SingleFileStoreConfigurationBuilder;
-import org.infinispan.configuration.cache.StorageType;
 import org.infinispan.configuration.global.GlobalConfigurationBuilder;
 import org.infinispan.manager.DefaultCacheManager;
 import org.infinispan.query.dsl.QueryFactory;
 import org.jboss.logging.Logger;
 
 import javax.enterprise.context.ApplicationScoped;
-import javax.enterprise.event.Observes;
 import javax.inject.Inject;
 import java.util.List;
 import java.util.Map;
@@ -55,7 +53,9 @@ public class InfinispanService {
 
     BasicCache<GroupedKey, ProjectFile> files;
 
-    BasicCache<GroupedKey, ProjectStatus> statuses;
+    BasicCache<GroupedKey, PipelineStatus> pipelineStatuses;
+
+    BasicCache<GroupedKey, DeploymentStatus> deploymentStatus;
 
     BasicCache<String, String> kamelets;
 
@@ -78,7 +78,7 @@ public class InfinispanService {
 
     private static final Logger LOGGER = 
Logger.getLogger(KaravanService.class.getName());
 
-    void onStart(@Observes StartupEvent ev) {
+    void start() {
         if (cacheManager == null) {
             LOGGER.info("InfinispanService is starting in local mode");
             GlobalConfigurationBuilder global = 
GlobalConfigurationBuilder.defaultClusteredBuilder();
@@ -94,13 +94,15 @@ public class InfinispanService {
                     .fetchPersistentState(true);
             projects = 
cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(Project.CACHE,
 builder.build());
             files = 
cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(ProjectFile.CACHE,
 builder.build());
-            statuses = 
cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(ProjectStatus.CACHE,
 builder.build());
+            pipelineStatuses = 
cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(PipelineStatus.CACHE,
 builder.build());
+            deploymentStatus = 
cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(DeploymentStatus.CACHE,
 builder.build());
             kamelets = 
cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(Kamelet.CACHE,
 builder.build());
         } else {
             LOGGER.info("InfinispanService is starting in remote mode");
             projects = 
cacheManager.administration().getOrCreateCache(Project.CACHE, new 
XMLStringConfiguration(String.format(CACHE_CONFIG, Project.CACHE)));
             files = 
cacheManager.administration().getOrCreateCache(ProjectFile.CACHE, new 
XMLStringConfiguration(String.format(CACHE_CONFIG, ProjectFile.CACHE)));
-            statuses = 
cacheManager.administration().getOrCreateCache(ProjectFile.CACHE, new 
XMLStringConfiguration(String.format(CACHE_CONFIG, ProjectStatus.CACHE)));
+            pipelineStatuses = 
cacheManager.administration().getOrCreateCache(ProjectFile.CACHE, new 
XMLStringConfiguration(String.format(CACHE_CONFIG, PipelineStatus.CACHE)));
+            deploymentStatus = 
cacheManager.administration().getOrCreateCache(ProjectFile.CACHE, new 
XMLStringConfiguration(String.format(CACHE_CONFIG, DeploymentStatus.CACHE)));
             kamelets = 
cacheManager.administration().getOrCreateCache(Kamelet.CACHE, new 
XMLStringConfiguration(String.format(CACHE_CONFIG, Kamelet.CACHE)));
         }
         if (getProjects().isEmpty()) {
@@ -164,12 +166,20 @@ public class InfinispanService {
         return projects.get(GroupedKey.create(project, project));
     }
 
-    public ProjectStatus getProjectStatus(String projectId) {
-        return statuses.get(GroupedKey.create(projectId, projectId));
+    public PipelineStatus getPipelineStatus(String projectId) {
+        return pipelineStatuses.get(GroupedKey.create(projectId, projectId));
+    }
+
+    public void savePipelineStatus(PipelineStatus status) {
+        pipelineStatuses.put(GroupedKey.create(status.getProjectId(), 
status.getProjectId()), status);
+    }
+
+    public DeploymentStatus getDeploymentStatus(String projectId) {
+        return deploymentStatus.get(GroupedKey.create(projectId, projectId));
     }
 
-    public void saveProjectStatus(ProjectStatus status) {
-        statuses.put(GroupedKey.create(status.getProjectId(), 
status.getProjectId()), status);
+    public void saveDeploymentStatus(DeploymentStatus status) {
+        deploymentStatus.put(GroupedKey.create(status.getProjectId(), 
status.getProjectId()), status);
     }
 
     public List<String> getKameletNames() {
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java
 
b/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java
index 8d43128..6a64e39 100644
--- 
a/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java
+++ 
b/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java
@@ -44,6 +44,9 @@ public class KaravanService {
     @Inject
     InfinispanService infinispanService;
 
+    @Inject
+    KubernetesService kubernetesService;
+
     @Inject
     GitService gitService;
 
@@ -54,6 +57,8 @@ public class KaravanService {
     KaravanConfiguration configuration;
 
     void onStart(@Observes StartupEvent ev) {
+        infinispanService.start();
+        kubernetesService.start();
     }
 
     @ConsumeEvent(value = IMPORT_PROJECTS, blocking = true)
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java
 
b/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java
index 92a583d..ed8e785 100644
--- 
a/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java
+++ 
b/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java
@@ -23,6 +23,7 @@ import io.fabric8.kubernetes.api.model.Secret;
 import io.fabric8.kubernetes.api.model.apps.Deployment;
 import io.fabric8.kubernetes.client.DefaultKubernetesClient;
 import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.Watch;
 import io.fabric8.kubernetes.client.dsl.LogWatch;
 import io.fabric8.openshift.api.model.ImageStream;
 import io.fabric8.openshift.client.OpenShiftClient;
@@ -35,24 +36,33 @@ import 
io.fabric8.tekton.pipeline.v1beta1.PipelineRunBuilder;
 import io.fabric8.tekton.pipeline.v1beta1.PipelineRunSpec;
 import io.fabric8.tekton.pipeline.v1beta1.PipelineRunSpecBuilder;
 import io.fabric8.tekton.pipeline.v1beta1.WorkspaceBindingBuilder;
+import io.quarkus.runtime.ShutdownEvent;
+import io.quarkus.runtime.StartupEvent;
 import io.vertx.mutiny.core.eventbus.EventBus;
 import org.apache.camel.karavan.model.DeploymentStatus;
+import org.apache.camel.karavan.model.KaravanConfiguration;
 import org.apache.camel.karavan.model.PipelineRunLog;
 import org.apache.camel.karavan.model.PodStatus;
 import org.apache.camel.karavan.model.Project;
+import org.apache.camel.karavan.watcher.DeploymentWatcher;
+import org.apache.camel.karavan.watcher.PipelineRunWatcher;
+import org.apache.camel.karavan.watcher.PodWatcher;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.jboss.logging.Logger;
 
 import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Observes;
 import javax.enterprise.inject.Produces;
 import javax.inject.Inject;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.channels.Pipe;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 @ApplicationScoped
@@ -64,6 +74,12 @@ public class KubernetesService {
     @ConfigProperty(name = "kubernetes.namespace", defaultValue = "localhost")
     String currentNamespace;
 
+    @Inject
+    KaravanConfiguration config;
+
+    @Inject
+    InfinispanService infinispanService;
+
     @Produces
     public KubernetesClient kubernetesClient() {
         return new DefaultKubernetesClient();
@@ -81,6 +97,29 @@ public class KubernetesService {
 
     private static final Logger LOGGER = 
Logger.getLogger(KubernetesService.class.getName());
 
+    private List<Watch> watches = new ArrayList<>();
+
+    void start() {
+        LOGGER.info("Start KubernetesService");
+        Optional<KaravanConfiguration.Environment> env = 
config.environments().stream()
+                .filter(environment -> 
environment.name().equals("dev")).findFirst();
+        if (env.isPresent()) {
+            
watches.add(kubernetesClient().apps().deployments().inNamespace(env.get().namespace())
+                    .watch(new DeploymentWatcher(infinispanService, this)));
+
+            
watches.add(kubernetesClient().pods().inNamespace(env.get().namespace()).withLabel("app.openshift.io/runtime",
 "camel")
+                    .watch(new PodWatcher(infinispanService, this)));
+
+            
watches.add(tektonClient().v1beta1().pipelineRuns().inNamespace(env.get().namespace())
+                    .watch(new PipelineRunWatcher(infinispanService)));
+        }
+    }
+
+    void onStop(@Observes ShutdownEvent ev) {
+        LOGGER.info("Stop KubernetesService");
+        watches.forEach(watch -> watch.close());
+    }
+
     public String createPipelineRun(Project project, String pipelineName, 
String namespace) throws Exception {
         LOGGER.info("Pipeline is creating for " + project.getProjectId());
 
@@ -189,41 +228,55 @@ public class KubernetesService {
         }
     }
 
-    public DeploymentStatus getDeploymentStatus(String name, String namespace) 
{
+    public DeploymentStatus getDeploymentStatus(String name, Deployment 
deployment) {
         try {
-            Deployment deployment = 
kubernetesClient().apps().deployments().inNamespace(namespace).withName(name).get();
-            if (deployment != null) {
-                String dsImage = 
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
-                String imageName = 
dsImage.startsWith("image-registry.openshift-image-registry.svc")
-                        ? 
dsImage.replace("image-registry.openshift-image-registry.svc:5000/", "")
-                        : dsImage;
-
-                List<Pod> pods = 
kubernetesClient().pods().inNamespace(namespace)
-                        .withLabel("app.kubernetes.io/name", name)
-                        .withLabel("app.openshift.io/runtime", "camel")
-                        .list().getItems();
-
-                List<PodStatus> podStatuses = pods.stream().map(pod -> new 
PodStatus(
-                        pod.getMetadata().getName(),
-                        
pod.getStatus().getContainerStatuses().get(0).getStarted(),
-                        
pod.getStatus().getContainerStatuses().get(0).getReady(),
-                        getPodReason(pod),
-                        
pod.getMetadata().getLabels().get("app.kubernetes.io/name")
-                )).collect(Collectors.toList());
-
-                return new DeploymentStatus(
-                        imageName,
-                        deployment.getSpec().getReplicas(),
-                        deployment.getStatus().getReadyReplicas(),
-                        deployment.getStatus().getUnavailableReplicas(),
-                        podStatuses
-                );
-            } else {
-                return new DeploymentStatus("", 0, 0, 0, List.of());
-            }
+            String dsImage = 
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
+            String imageName = 
dsImage.startsWith("image-registry.openshift-image-registry.svc")
+                    ? 
dsImage.replace("image-registry.openshift-image-registry.svc:5000/", "")
+                    : dsImage;
+
+            List<PodStatus> podStatuses = getDeploymentPodsStatuses(name, 
deployment.getMetadata().getNamespace());
+
+            return new DeploymentStatus(
+                    name,
+                    imageName,
+                    deployment.getSpec().getReplicas(),
+                    deployment.getStatus().getReadyReplicas(),
+                    deployment.getStatus().getUnavailableReplicas(),
+                    podStatuses
+            );
+        } catch (Exception ex) {
+            LOGGER.error(ex.getMessage());
+            return new DeploymentStatus(name);
+        }
+    }
+
+    public List<PodStatus> getDeploymentPodsStatuses(String name, String 
namespace) {
+        try {
+            List<Pod> pods = kubernetesClient().pods().inNamespace(namespace)
+                    .withLabel("app.kubernetes.io/name", name)
+                    .withLabel("app.openshift.io/runtime", "camel")
+                    .list().getItems();
+
+            return pods.stream().map(pod -> new PodStatus(
+                    pod.getMetadata().getName(),
+                    pod.getStatus().getContainerStatuses().get(0).getStarted(),
+                    pod.getStatus().getContainerStatuses().get(0).getReady(),
+                    getPodReason(pod),
+                    pod.getMetadata().getLabels().get("app.kubernetes.io/name")
+            )).collect(Collectors.toList());
+        } catch (Exception ex) {
+            LOGGER.error(ex.getMessage());
+            return List.of();
+        }
+    }
+
+    public Deployment getDeployment(String name, String namespace) {
+        try {
+            return 
kubernetesClient().apps().deployments().inNamespace(namespace).withName(name).get();
         } catch (Exception ex) {
             LOGGER.error(ex.getMessage());
-            return new DeploymentStatus();
+            return null;
         }
     }
 
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java 
b/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java
index a947236..fa5329a 100644
--- 
a/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java
+++ 
b/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java
@@ -17,30 +17,19 @@
 package org.apache.camel.karavan.service;
 
 import io.fabric8.tekton.pipeline.v1beta1.PipelineRun;
-import io.quarkus.runtime.configuration.ProfileManager;
-import io.quarkus.scheduler.Scheduled;
 import io.quarkus.vertx.ConsumeEvent;
 import io.smallrye.mutiny.tuples.Tuple4;
 import io.vertx.core.json.JsonObject;
 import io.vertx.mutiny.core.Vertx;
-import io.vertx.mutiny.core.buffer.Buffer;
-import io.vertx.mutiny.ext.web.client.HttpResponse;
 import io.vertx.mutiny.ext.web.client.WebClient;
-import org.apache.camel.karavan.model.DeploymentStatus;
 import org.apache.camel.karavan.model.KaravanConfiguration;
 import org.apache.camel.karavan.model.ProjectEnvStatus;
-import org.apache.camel.karavan.model.ProjectStatus;
 import org.jboss.logging.Logger;
 
-
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import java.time.Instant;
-import java.util.ArrayList;
 import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.stream.Collectors;
 
 @ApplicationScoped
 public class StatusService {
@@ -71,66 +60,47 @@ public class StatusService {
         return webClient;
     }
 
-    @Scheduled(every="10s")
-    void checkDeployedProjects() {
-        LOGGER.info("Check deployed projects");
-        infinispanService.getProjects().forEach(project -> {
-            Optional<KaravanConfiguration.Environment> env = 
configuration.environments().stream().filter(environment -> 
environment.name().equals("dev")).findFirst();
-            if (env.isPresent()) {
-                boolean hasDeployment =  
kubernetesService.hasDeployment(project.getProjectId(), env.get().namespace());
-                if (!project.getDeployed() && hasDeployment) {
-                    project.setDeployed(true);
-                    infinispanService.saveProject(project);
-                } else if (project.getDeployed() && !hasDeployment) {
-                    project.setDeployed(false);
-                    infinispanService.saveProject(project);
-                    infinispanService.saveProjectStatus(new 
ProjectStatus(project.getProjectId(), List.of(), System.currentTimeMillis()));
-                }
-            }
-        });
-    }
-
 
     @ConsumeEvent(value = CMD_COLLECT_STATUSES, blocking = true, ordered = 
true)
     public void collectStatuses(String projectId) throws Exception {
         if ((System.currentTimeMillis() - lastCollect) > 
configuration.statusThreshold()) {
-            collectStatusesForProject(projectId);
+//            collectStatusesForProject(projectId);
             lastCollect = System.currentTimeMillis();
         }
     }
 
-    private void collectStatusesForProject(String projectId) {
-        ProjectStatus old = infinispanService.getProjectStatus(projectId);
-        ProjectStatus status = new ProjectStatus();
-        status.setProjectId(projectId);
-        status.setLastUpdate(System.currentTimeMillis());
-        List<ProjectEnvStatus> statuses = new ArrayList<>(1);
-        configuration.environments().stream().filter(e -> 
e.active()).forEach(e -> {
-            String url = ProfileManager.getActiveProfile().equals("dev")
-                    ? String.format("http://%s-%s.%s/q/health";, projectId, 
e.namespace(), e.cluster())
-                    : String.format("http://%s.%s.%s/q/health";, projectId, 
e.namespace(), e.cluster());
-            ProjectEnvStatus pes = getProjectEnvStatus(url, e.name());
-            DeploymentStatus ds = 
kubernetesService.getDeploymentStatus(projectId, e.namespace());
-            Tuple4<Boolean, String, String, Long> pipeline = 
getProjectPipelineStatus(projectId, e.pipeline(), e.namespace());
-
-            pes.setDeploymentStatus(ds);
-
-            if (pipeline.getItem1()){
-                pes.setLastPipelineRun(pipeline.getItem2());
-                pes.setLastPipelineRunResult(pipeline.getItem3());
-                pes.setLastPipelineRunTime(pipeline.getItem4());
-            } else if (old != null){
-                Optional<ProjectEnvStatus> opes = 
old.getStatuses().stream().filter(x -> 
x.getEnvironment().equals(e.name())).findFirst();
-                if (opes.isPresent()) {
-                    pes.setLastPipelineRun(opes.get().getLastPipelineRun());
-                    
pes.setLastPipelineRunResult(opes.get().getLastPipelineRunResult());
-                }
-            }
-            statuses.add(pes);
-        });
-        status.setStatuses(statuses);
-        infinispanService.saveProjectStatus(status);
-    }
+//    private void collectStatusesForProject(String projectId) {
+//        ProjectStatus old = infinispanService.getProjectStatus(projectId);
+//        ProjectStatus status = new ProjectStatus();
+//        status.setProjectId(projectId);
+//        status.setLastUpdate(System.currentTimeMillis());
+//        List<ProjectEnvStatus> statuses = new ArrayList<>(1);
+//        configuration.environments().stream().filter(e -> 
e.active()).forEach(e -> {
+//            String url = ProfileManager.getActiveProfile().equals("dev")
+//                    ? String.format("http://%s-%s.%s/q/health";, projectId, 
e.namespace(), e.cluster())
+//                    : String.format("http://%s.%s.%s/q/health";, projectId, 
e.namespace(), e.cluster());
+//            ProjectEnvStatus pes = getProjectEnvStatus(url, e.name());
+//            DeploymentStatus ds = 
kubernetesService.getDeploymentStatus(projectId, e.namespace());
+//            Tuple4<Boolean, String, String, Long> pipeline = 
getProjectPipelineStatus(projectId, e.pipeline(), e.namespace());
+//
+//            pes.setDeploymentStatus(ds);
+//
+//            if (pipeline.getItem1()){
+//                pes.setLastPipelineRun(pipeline.getItem2());
+//                pes.setLastPipelineRunResult(pipeline.getItem3());
+//                pes.setLastPipelineRunTime(pipeline.getItem4());
+//            } else if (old != null){
+//                Optional<ProjectEnvStatus> opes = 
old.getStatuses().stream().filter(x -> 
x.getEnvironment().equals(e.name())).findFirst();
+//                if (opes.isPresent()) {
+//                    pes.setLastPipelineRun(opes.get().getLastPipelineRun());
+//                    
pes.setLastPipelineRunResult(opes.get().getLastPipelineRunResult());
+//                }
+//            }
+//            statuses.add(pes);
+//        });
+//        status.setStatuses(statuses);
+//        infinispanService.saveProjectStatus(status);
+//    }
 
     private Tuple4<Boolean, String, String, Long> 
getProjectPipelineStatus(String projectId, String pipelineName, String 
namespace) {
         try {
@@ -152,37 +122,37 @@ public class StatusService {
         }
     }
 
-    private ProjectEnvStatus getProjectEnvStatus(String url, String env) {
-        // TODO: make it reactive
-        try {
-            HttpResponse<Buffer> result = 
getWebClient().getAbs(url).timeout(1000).send().subscribeAsCompletionStage().toCompletableFuture().get();
-            if (result.statusCode() == 200) {
-                JsonObject res = result.bodyAsJsonObject();
-                List<JsonObject> checks = 
res.getJsonArray("checks").stream().map(o -> 
(JsonObject)o).collect(Collectors.toList());
-
-                JsonObject context = checks.stream().filter(o -> 
Objects.equals(o.getString("name"), "context")).findFirst().get();
-                return new ProjectEnvStatus(
-                        env,
-                        res != null && res.containsKey("status") && 
res.getString("status").equals("UP") ? ProjectEnvStatus.Status.UP : 
ProjectEnvStatus.Status.DOWN,
-                        getStatus(checks, "context"),
-                        getStatus(checks, "camel-consumers"),
-                        getStatus(checks, "camel-routes"),
-                        getStatus(checks, "camel-registry"),
-                        
context.getJsonObject("data").getString("context.version"),
-                "",
-                "",
-                0L,
-                new DeploymentStatus()
-                );
-            } else {
-                return new ProjectEnvStatus(env);
-            }
-        } catch (Exception ex) {
-            LOGGER.error(ex.getMessage());
-//            ex.printStackTrace();
-            return new ProjectEnvStatus(env);
-        }
-    }
+//    private ProjectEnvStatus getProjectEnvStatus(String url, String env) {
+//        // TODO: make it reactive
+//        try {
+//            HttpResponse<Buffer> result = 
getWebClient().getAbs(url).timeout(1000).send().subscribeAsCompletionStage().toCompletableFuture().get();
+//            if (result.statusCode() == 200) {
+//                JsonObject res = result.bodyAsJsonObject();
+//                List<JsonObject> checks = 
res.getJsonArray("checks").stream().map(o -> 
(JsonObject)o).collect(Collectors.toList());
+//
+//                JsonObject context = checks.stream().filter(o -> 
Objects.equals(o.getString("name"), "context")).findFirst().get();
+//                return new ProjectEnvStatus(
+//                        env,
+//                        res != null && res.containsKey("status") && 
res.getString("status").equals("UP") ? ProjectEnvStatus.Status.UP : 
ProjectEnvStatus.Status.DOWN,
+//                        getStatus(checks, "context"),
+//                        getStatus(checks, "camel-consumers"),
+//                        getStatus(checks, "camel-routes"),
+//                        getStatus(checks, "camel-registry"),
+//                        
context.getJsonObject("data").getString("context.version"),
+//                "",
+//                "",
+//                0L,
+//                new DeploymentStatus()
+//                );
+//            } else {
+//                return new ProjectEnvStatus(env);
+//            }
+//        } catch (Exception ex) {
+//            LOGGER.error(ex.getMessage());
+////            ex.printStackTrace();
+//            return new ProjectEnvStatus(env);
+//        }
+//    }
 
     private ProjectEnvStatus.Status getStatus(List<JsonObject> checks, String 
name){
         try {
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/watcher/DeploymentWatcher.java
 
b/karavan-app/src/main/java/org/apache/camel/karavan/watcher/DeploymentWatcher.java
new file mode 100644
index 0000000..8f635f3
--- /dev/null
+++ 
b/karavan-app/src/main/java/org/apache/camel/karavan/watcher/DeploymentWatcher.java
@@ -0,0 +1,59 @@
+package org.apache.camel.karavan.watcher;
+
+import io.fabric8.kubernetes.client.Watcher;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.client.WatcherException;
+import org.apache.camel.karavan.model.DeploymentStatus;
+import org.apache.camel.karavan.model.PodStatus;
+import org.apache.camel.karavan.model.Project;
+import org.apache.camel.karavan.service.InfinispanService;
+import org.apache.camel.karavan.service.KubernetesService;
+import org.jboss.logging.Logger;
+
+import java.util.List;
+
+public class DeploymentWatcher implements Watcher<Deployment> {
+
+    private static final Logger LOGGER = 
Logger.getLogger(DeploymentWatcher.class.getName());
+    private InfinispanService infinispanService;
+    private KubernetesService kubernetesService;
+
+    public DeploymentWatcher(InfinispanService infinispanService, 
KubernetesService kubernetesService) {
+        this.infinispanService = infinispanService;
+        this.kubernetesService = kubernetesService;
+    }
+
+    @Override
+    public void eventReceived(Watcher.Action action, Deployment deployment) {
+        LOGGER.info(action.name() + " " + deployment.getMetadata().getName());
+        Project project = 
infinispanService.getProject(deployment.getMetadata().getName());
+        if (project != null) {
+            switch (action.name()) {
+                case "ADDED":
+                    project.setDeployed(true);
+                    infinispanService.saveProject(project);
+                    DeploymentStatus s = 
kubernetesService.getDeploymentStatus(project.getProjectId(), deployment);
+                    infinispanService.saveDeploymentStatus(s);
+                    break;
+                case "MODIFIED":
+                    if (!project.getDeployed()) {
+                        project.setDeployed(true);
+                        infinispanService.saveProject(project);
+                    }
+                    DeploymentStatus ds = 
kubernetesService.getDeploymentStatus(project.getProjectId(), deployment);
+                    infinispanService.saveDeploymentStatus(ds);
+                    break;
+                case "DELETED":
+                    project.setDeployed(false);
+                    infinispanService.saveProject(project);
+                    infinispanService.saveDeploymentStatus(new 
DeploymentStatus(project.getProjectId()));
+                    break;
+            }
+        }
+    }
+
+    @Override
+    public void onClose(WatcherException cause) {
+
+    }
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/watcher/PipelineRunWatcher.java
 
b/karavan-app/src/main/java/org/apache/camel/karavan/watcher/PipelineRunWatcher.java
new file mode 100644
index 0000000..df966a3
--- /dev/null
+++ 
b/karavan-app/src/main/java/org/apache/camel/karavan/watcher/PipelineRunWatcher.java
@@ -0,0 +1,74 @@
+package org.apache.camel.karavan.watcher;
+
+import io.fabric8.kubernetes.client.Watcher;
+import io.fabric8.kubernetes.client.WatcherException;
+import io.fabric8.tekton.pipeline.v1beta1.PipelineRun;
+import org.apache.camel.karavan.model.PipelineStatus;
+import org.apache.camel.karavan.model.Project;
+import org.apache.camel.karavan.service.InfinispanService;
+import org.jboss.logging.Logger;
+
+import java.time.Instant;
+import java.util.List;
+
+public class PipelineRunWatcher implements Watcher<PipelineRun> {
+
+    private static final Logger LOGGER = 
Logger.getLogger(PipelineRunWatcher.class.getName());
+    private InfinispanService infinispanService;
+
+    public PipelineRunWatcher(InfinispanService infinispanService) {
+        this.infinispanService = infinispanService;
+    }
+
+    @Override
+    public void eventReceived(Action action, PipelineRun pipelineRun) {
+        LOGGER.info(action.name() + " " + pipelineRun.getMetadata().getName());
+        String projectId = 
pipelineRun.getMetadata().getLabels().get("karavan-project-id");
+        if (projectId != null) {
+            Project project = infinispanService.getProject(projectId);
+            if (project != null && List.of("MODIFIED", 
"ADDED").contains(action.name())) {
+                PipelineStatus pipelineStatus = 
infinispanService.getPipelineStatus(projectId);
+                if (pipelineStatus == null) pipelineStatus = new 
PipelineStatus(project.getProjectId());
+
+                if (pipelineRun.getStatus() != null) {
+                    LOGGER.info(action.name()+ " " + 
pipelineRun.getMetadata().getName() + " " + 
pipelineRun.getStatus().getConditions().get(0).getReason());
+                    Instant runStartTime = 
Instant.parse(pipelineRun.getStatus().getStartTime());
+                    Instant savedStartTime = pipelineStatus.getStartTime() != 
null
+                            ? Instant.parse(pipelineStatus.getStartTime())
+                            : Instant.MIN;
+
+                    if (runStartTime.isAfter(savedStartTime) || 
pipelineRun.getMetadata().getName().equals(pipelineStatus.getPipelineName())) {
+                        
pipelineStatus.setPipelineName(pipelineRun.getMetadata().getName());
+                        
pipelineStatus.setResult(pipelineRun.getStatus().getConditions().get(0).getReason());
+                        
pipelineStatus.setStartTime(pipelineRun.getStatus().getStartTime());
+                        
pipelineStatus.setCompletionTime(pipelineRun.getStatus().getCompletionTime());
+                    }
+                } else {
+                    
pipelineStatus.setPipelineName(pipelineRun.getMetadata().getName());
+                    pipelineStatus.setResult(null);
+                    pipelineStatus.setStartTime(null);
+                    pipelineStatus.setCompletionTime(null);
+                }
+                infinispanService.savePipelineStatus(pipelineStatus);
+            }
+        }
+    }
+
+    private Long getPipelineRunDuration(PipelineRun pipelineRun) {
+        try {
+            Instant create = 
Instant.parse(pipelineRun.getMetadata().getCreationTimestamp());
+            Instant completion = pipelineRun.getStatus().getCompletionTime() 
!= null
+                    ? 
Instant.parse(pipelineRun.getStatus().getCompletionTime())
+                    : Instant.now();
+            return completion.getEpochSecond() - create.getEpochSecond();
+        } catch (Exception ex) {
+            LOGGER.error(ex.getMessage());
+            return 0L;
+        }
+    }
+
+    @Override
+    public void onClose(WatcherException cause) {
+
+    }
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/watcher/PodWatcher.java 
b/karavan-app/src/main/java/org/apache/camel/karavan/watcher/PodWatcher.java
new file mode 100644
index 0000000..23fb200
--- /dev/null
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/watcher/PodWatcher.java
@@ -0,0 +1,40 @@
+package org.apache.camel.karavan.watcher;
+
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.client.Watcher;
+import io.fabric8.kubernetes.client.WatcherException;
+import org.apache.camel.karavan.model.DeploymentStatus;
+import org.apache.camel.karavan.model.Project;
+import org.apache.camel.karavan.service.InfinispanService;
+import org.apache.camel.karavan.service.KubernetesService;
+import org.jboss.logging.Logger;
+
+public class PodWatcher implements Watcher<Pod> {
+
+    private static final Logger LOGGER = 
Logger.getLogger(PodWatcher.class.getName());
+    private InfinispanService infinispanService;
+    private KubernetesService kubernetesService;
+
+    public PodWatcher(InfinispanService infinispanService, KubernetesService 
kubernetesService) {
+        this.infinispanService = infinispanService;
+        this.kubernetesService = kubernetesService;
+    }
+
+    @Override
+    public void eventReceived(Action action, Pod pod) {
+        LOGGER.info(action.name() + " " + pod.getMetadata().getName());
+        String name = 
pod.getMetadata().getLabels().get("app.kubernetes.io/name");
+        Project project = infinispanService.getProject(name);
+        Deployment deployment = kubernetesService.getDeployment(name, 
pod.getMetadata().getNamespace());
+        if (project != null && deployment != null) {
+            DeploymentStatus s = 
kubernetesService.getDeploymentStatus(project.getProjectId(), deployment);
+            infinispanService.saveDeploymentStatus(s);
+        }
+    }
+
+    @Override
+    public void onClose(WatcherException cause) {
+
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/resources/application.properties 
b/karavan-app/src/main/resources/application.properties
index 63e986e..bc96a84 100644
--- a/karavan-app/src/main/resources/application.properties
+++ b/karavan-app/src/main/resources/application.properties
@@ -1,8 +1,5 @@
 karavan.version=3.18.4
 
-karavan.master-username=admin
-karavan.master-password=karavan
-
 # Git repository Configuration
 karavan.projects-git-repository=${GIT_REPOSITORY}
 karavan.projects-git-username=${GIT_USERNAME}
@@ -40,6 +37,10 @@ karavan.config.environments[2].pipeline=karavan-quarkus
 karavan.config.environments[2].cluster=svc.cluster.local
 karavan.config.environments[2].active=false
 
+%dev.karavan.config.environments[0].cluster=apps.home.myocp.net 
+%dev.karavan.config.environments[1].cluster=apps.home.myocp.net
+%dev.karavan.config.environments[2].cluster=apps.home.myocp.net
+
 # Infinispan Server address
 #quarkus.infinispan-client.server-list=localhost:12345
 quarkus.infinispan-client.devservices.enabled=false
@@ -53,6 +54,13 @@ quarkus.infinispan-client.auth-password=password
 # Use BASIC as a Docker for Mac workaround
 quarkus.infinispan-client.client-intelligence=BASIC
 
+# Public Dev
+%dev.karavan.auth=public
+%dev.quarkus.oidc.enabled=false
+%dev.quarkus.http.auth.basic=false
+%dev.quarkus.security.users.embedded.enabled=false
+%dev.quarkus.http.auth.permission.authenticated.enabled=false
+%dev.quarkus.http.auth.permission.public.enabled=false
 
 # Public
 %public.karavan.auth=public
diff --git a/karavan-app/src/main/webapp/src/api/KaravanApi.tsx 
b/karavan-app/src/main/webapp/src/api/KaravanApi.tsx
index a7031e9..4441d52 100644
--- a/karavan-app/src/main/webapp/src/api/KaravanApi.tsx
+++ b/karavan-app/src/main/webapp/src/api/KaravanApi.tsx
@@ -1,5 +1,5 @@
 import axios, {AxiosResponse} from "axios";
-import {Project, ProjectFile, ProjectStatus} from "../projects/ProjectModels";
+import {DeploymentStatus, PipelineStatus, Project, ProjectFile} from 
"../projects/ProjectModels";
 import {Buffer} from 'buffer';
 import {SsoApi} from "./SsoApi";
 
@@ -149,8 +149,19 @@ export class KaravanApi {
         });
     }
 
-    static async getProjectStatus(projectId: string, after: (status: 
ProjectStatus) => void) {
-        instance.get('/api/status/project/' + projectId)
+    static async getProjectPipelineStatus(projectId: string, after: (status: 
PipelineStatus) => void) {
+        instance.get('/api/status/pipeline/' + projectId)
+            .then(res => {
+                if (res.status === 200) {
+                    after(res.data);
+                }
+            }).catch(err => {
+            console.log(err);
+        });
+    }
+
+    static async getProjectDeploymentStatus(projectId: string, after: (status: 
DeploymentStatus) => void) {
+        instance.get('/api/status/deployment/' + projectId)
             .then(res => {
                 if (res.status === 200) {
                     after(res.data);
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx 
b/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx
index c40d1d6..b3dffe3 100644
--- a/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx
+++ b/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx
@@ -7,7 +7,7 @@ import {
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
 import {KaravanApi} from "../api/KaravanApi";
-import {Project, ProjectFileTypes, ProjectStatus} from "./ProjectModels";
+import {DeploymentStatus, Project, ProjectFileTypes} from "./ProjectModels";
 import {ChartDonutThreshold} from "@patternfly/react-charts";
 
 interface Props {
@@ -18,7 +18,7 @@ interface Props {
 
 interface State {
     project?: Project,
-    status?: ProjectStatus,
+    status?: DeploymentStatus,
     key?: string,
 }
 
@@ -50,13 +50,13 @@ export class ProjectDashboard extends 
React.Component<Props, State> {
 
     onRefreshStatus = () => {
         if (this.props.project) {
-            KaravanApi.getProjectStatus(this.props.project.projectId, (status: 
ProjectStatus) => {
-                this.setState({
-                    key: Math.random().toString(),
-                    status: status
-                });
-                // console.log(status);
-            });
+            // KaravanApi.getProjectStatus(this.props.project.projectId, 
(status: ProjectStatus) => {
+            //     this.setState({
+            //         key: Math.random().toString(),
+            //         status: status
+            //     });
+            //     // console.log(status);
+            // });
         }
     }
 
@@ -70,47 +70,47 @@ export class ProjectDashboard extends 
React.Component<Props, State> {
         }
     }
 
-    isUp(env: string): boolean {
-        if (this.state.status) {
-            return this.state.status.statuses.find(s => s.environment === 
env)?.status === 'UP';
-        } else {
-            return false;
-        }
-    }
+    // isUp(env: string): boolean {
+    //     if (this.state.status) {
+    //         return this.state.status.statuses.find(s => s.environment === 
env)?.status === 'UP';
+    //     } else {
+    //         return false;
+    //     }
+    // }
 
     getEnvironmentData(env: string) {
-        const pes  = this.state.status?.statuses.find(s => s.environment == 
env);
-        if (pes){
-            const replicas = pes.deploymentStatus.replicas;
-            const data = Array.from({length: replicas}, (v, k) => {
-                return { x: k, y: 100/replicas }
-            });
-            const unavailableReplicas = 
pes.deploymentStatus.unavailableReplicas;
-            const dataU = Array.from({length: unavailableReplicas}, (v, k) => {
-                return { x: k, y: 100/unavailableReplicas }
-            });
-            const readyReplicas = pes.deploymentStatus.readyReplicas;
-            const colorScale = Array.from({length: replicas}, (v, k) => {
-                if (k < readyReplicas) return "rgb(56, 129, 47)"
-                else return "#8bc1f7"
-            })
-            return (
-                <div style={{ height: '185px', width: '185px' }}>
-                    <ChartDonutThreshold
-                        constrainToVisibleArea
-                        data={data}
-                        colorScale={colorScale}
-                        height={185}
-                        width={185}
-                    >
-                        <ChartDonutThreshold
-                            data={dataU}
-                            title="Pods"
-                        />
-                    </ChartDonutThreshold>
-                </div>
-            );
-        }
+        // const pes  = this.state.status?.statuses.find(s => s.environment == 
env);
+        // if (pes){
+        //     const replicas = pes.deploymentStatus.replicas;
+        //     const data = Array.from({length: replicas}, (v, k) => {
+        //         return { x: k, y: 100/replicas }
+        //     });
+        //     const unavailableReplicas = 
pes.deploymentStatus.unavailableReplicas;
+        //     const dataU = Array.from({length: unavailableReplicas}, (v, k) 
=> {
+        //         return { x: k, y: 100/unavailableReplicas }
+        //     });
+        //     const readyReplicas = pes.deploymentStatus.readyReplicas;
+        //     const colorScale = Array.from({length: replicas}, (v, k) => {
+        //         if (k < readyReplicas) return "rgb(56, 129, 47)"
+        //         else return "#8bc1f7"
+        //     })
+        //     return (
+        //         <div style={{ height: '185px', width: '185px' }}>
+        //             <ChartDonutThreshold
+        //                 constrainToVisibleArea
+        //                 data={data}
+        //                 colorScale={colorScale}
+        //                 height={185}
+        //                 width={185}
+        //             >
+        //                 <ChartDonutThreshold
+        //                     data={dataU}
+        //                     title="Pods"
+        //                 />
+        //             </ChartDonutThreshold>
+        //         </div>
+        //     );
+        // }
     }
 
     render() {
@@ -124,10 +124,10 @@ export class ProjectDashboard extends 
React.Component<Props, State> {
                                 <CardBody>
                                     <Flex direction={{default: "row"}} 
alignItems={{default:"alignItemsCenter"}}>
                                         <FlexItem>
-                                            <Badge className={this.isUp(e) ? 
"badge-env-up" : ""} isRead>{e}</Badge>
+                                            {/*<Badge className={this.isUp(e) 
? "badge-env-up" : ""} isRead>{e}</Badge>*/}
                                         </FlexItem>
                                         <FlexItem>
-                                            {this.getEnvironmentData(e)}
+                                            {/*{this.getEnvironmentData(e)}*/}
                                         </FlexItem>
                                     </Flex>
                                 </CardBody>
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx 
b/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx
index 74427e4..64e089b 100644
--- a/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx
+++ b/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx
@@ -11,7 +11,7 @@ import {
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
 import {KaravanApi} from "../api/KaravanApi";
-import {DeploymentStatus, Project, ProjectStatus} from "./ProjectModels";
+import {DeploymentStatus, Project, PipelineStatus, CamelStatus} from 
"./ProjectModels";
 import BuildIcon from "@patternfly/react-icons/dist/esm/icons/build-icon";
 import RolloutIcon from 
"@patternfly/react-icons/dist/esm/icons/process-automation-icon";
 import PushIcon from "@patternfly/react-icons/dist/esm/icons/code-branch-icon";
@@ -29,7 +29,9 @@ interface Props {
 
 interface State {
     project?: Project,
-    status?: ProjectStatus,
+    pipelineStatus?: PipelineStatus,
+    deploymentStatus?: DeploymentStatus,
+    camelStatus?: CamelStatus,
     isPushing: boolean,
     isBuilding: boolean,
     isRolling: boolean,
@@ -77,12 +79,14 @@ export class ProjectInfo extends React.Component<Props, 
State> {
     }
 
     onRefreshStatus = () => {
+        // console.log("onRefreshStatus", this.props.project)
         if (this.props.project) {
-            KaravanApi.getProjectStatus(this.props.project.projectId, (status: 
ProjectStatus) => {
-                this.setState({
-                    key: Math.random().toString(),
-                    status: status
-                });
+            KaravanApi.getProjectPipelineStatus(this.props.project.projectId, 
(status: PipelineStatus) => {
+                this.setState({key: Math.random().toString(), pipelineStatus: 
status});
+                // console.log(status);
+            });
+            
KaravanApi.getProjectDeploymentStatus(this.props.project.projectId, (status: 
DeploymentStatus) => {
+                this.setState({key: Math.random().toString(), 
deploymentStatus: status});
                 // console.log(status);
             });
         }
@@ -141,20 +145,17 @@ export class ProjectInfo extends React.Component<Props, 
State> {
     }
 
     buildButton = (env: string) => {
-        const isDeploying = this.state.isBuilding;
-        const isPushing = this.state.isPushing;
-        const status = this.state.status?.statuses.find(s => s.environment === 
env)
-        const pipelineResult = status?.lastPipelineRunResult;
-        const isRunning = pipelineResult === 'Running';
+        const {isBuilding, isPushing, pipelineStatus} = this.state;
+        const isRunning = pipelineStatus?.result === 'Running';
         return (<Tooltip content="Build and deploy" position={"left"}>
-            <Button isLoading={isDeploying ? true : undefined}
-                    isDisabled={isDeploying || isRunning || isPushing}
+            <Button isLoading={isBuilding ? true : undefined}
+                    isDisabled={isBuilding || isRunning || isPushing}
                     isSmall
                     variant="secondary"
                     className="project-button"
-                    icon={!isDeploying ? <BuildIcon/> : <div></div>}
+                    icon={!isBuilding ? <BuildIcon/> : <div></div>}
                     onClick={e => this.build()}>
-                {isDeploying ? "..." : "Deploy"}
+                {isBuilding ? "..." : "Deploy"}
             </Button>
         </Tooltip>)
     }
@@ -203,8 +204,7 @@ export class ProjectInfo extends React.Component<Props, 
State> {
     }
 
     getEnvPanel(env: string) {
-        const {status, project} = this.state;
-        const deploymentStatus = status?.statuses.find(s => s.environment === 
env)?.deploymentStatus;
+        const {deploymentStatus} = this.state;
         return (
             <DescriptionList isHorizontal>
                 <DescriptionListGroup>
@@ -305,7 +305,7 @@ export class ProjectInfo extends React.Component<Props, 
State> {
     }
 
     getHealthPanel(env: string) {
-        const status = this.state.status?.statuses.find(s => s.environment === 
env)
+        const status = this.state.camelStatus;
         const registryStatus = status?.registryStatus;
         const routesStatus = status?.routesStatus;
         const consumersStatus = status?.consumerStatus;
@@ -325,10 +325,16 @@ export class ProjectInfo extends React.Component<Props, 
State> {
     }
 
     getPipelineState(env: string) {
-        const status = this.state.status?.statuses.find(s => s.environment === 
env)
-        const pipeline = status?.lastPipelineRun;
-        const pipelineResult = status?.lastPipelineRunResult;
-        const lastPipelineRunTime = status?.lastPipelineRunTime;
+        // console.log(this.state.pipelineStatus)
+        const status = this.state.pipelineStatus;
+        const pipeline = status?.pipelineName;
+        const pipelineResult = status?.result;
+        let lastPipelineRunTime = 0;
+        if (status?.startTime){
+            const start:Date = new Date(status.startTime);
+            const finish:Date = status.completionTime != undefined && 
status.completionTime != null ? new Date(status.completionTime) : new Date();
+            lastPipelineRunTime =Math.round( (finish.getTime() - 
start.getTime()) / 1000 );
+        }
         const showTime = lastPipelineRunTime && lastPipelineRunTime > 0;
         const isRunning = pipelineResult === 'Running';
         const isFailed = pipelineResult === 'Failed';
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectModels.ts 
b/karavan-app/src/main/webapp/src/projects/ProjectModels.ts
index 2d98870..11bf478 100644
--- a/karavan-app/src/main/webapp/src/projects/ProjectModels.ts
+++ b/karavan-app/src/main/webapp/src/projects/ProjectModels.ts
@@ -8,7 +8,7 @@ export class Project {
     public constructor(projectId: string, name: string, description: string, 
lastCommit: string);
     public constructor(init?: Partial<Project>);
     public constructor(...args: any[]) {
-        if (args.length === 1){
+        if (args.length === 1) {
             Object.assign(this, args[0]);
             return;
         } else {
@@ -21,21 +21,8 @@ export class Project {
     }
 }
 
-export class ProjectEnvStatus {
-    environment: string = '';
-    status: string = '';
-    contextStatus: string = '';
-    consumerStatus: string = '';
-    routesStatus: string = '';
-    registryStatus: string = '';
-    contextVersion: string = '';
-    lastPipelineRun: string = '';
-    lastPipelineRunResult: string = '';
-    lastPipelineRunTime: number = 0;
-    deploymentStatus: DeploymentStatus = new DeploymentStatus();
-}
-
 export class DeploymentStatus {
+    projectId: string = '';
     image: string = '';
     replicas: number = 0;
     readyReplicas: number = 0;
@@ -51,10 +38,21 @@ export class PodStatus {
     deployment: string = '';
 }
 
-export class ProjectStatus {
+export class CamelStatus {
+    projectId: string = '';
+    registryStatus: string = '';
+    routesStatus: string = '';
+    consumerStatus: string = '';
+    contextStatus: string = '';
+    contextVersion: string = '';
+}
+
+export class PipelineStatus {
     projectId: string = '';
-    lastUpdate: number = 0;
-    statuses: ProjectEnvStatus[] = [];
+    pipelineName: string = '';
+    result: string = '';
+    startTime: string = '';
+    completionTime: string = '';
 }
 
 export class ProjectFile {
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx 
b/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx
index cc913a8..abe01a4 100644
--- a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx
+++ b/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx
@@ -26,7 +26,7 @@ import {
 import '../designer/karavan.css';
 import {MainToolbar} from "../MainToolbar";
 import {KaravanApi} from "../api/KaravanApi";
-import {Project, ProjectFile, ProjectFileTypes, ProjectStatus} from 
"./ProjectModels";
+import {Project, ProjectFile, ProjectFileTypes} from "./ProjectModels";
 import {CamelUi} from "../designer/utils/CamelUi";
 import UploadIcon from "@patternfly/react-icons/dist/esm/icons/upload-icon";
 import {TableComposable, Tbody, Td, Th, Thead, Tr} from 
"@patternfly/react-table";
@@ -55,7 +55,6 @@ interface Props {
 
 interface State {
     project?: Project,
-    status?: ProjectStatus,
     file?: ProjectFile,
     files: ProjectFile[],
     isUploadModalOpen: boolean,
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx 
b/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx
index 798fdd3..d7c65c8 100644
--- a/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx
+++ b/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx
@@ -194,7 +194,6 @@ export class ProjectsPage extends React.Component<Props, 
State> {
     }
 
     deleteModalForm() {
-        const {isCopy, projectToCopy, projectId, name, isCreateModalOpen} = 
this.state;
         return (
             <Modal
                 title="Confirmation"
@@ -258,8 +257,7 @@ export class ProjectsPage extends React.Component<Props, 
State> {
                                     </Td>
                                     <Td noPadding style={{width:"180px"}}>
                                         <Flex direction={{default: "row"}}>
-                                            {environments.filter(e => e !== 
undefined)
-                                                .map(e => <FlexItem 
key={e}><Badge isRead={!project.deployed}>{e}</Badge></FlexItem>)}
+                                            <FlexItem key={"dev"}><Badge 
isRead={!project.deployed}>{"dev"}</Badge></FlexItem>
                                         </Flex>
                                     </Td>
                                     <Td isActionCell>

Reply via email to