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/05/11 15:27:34 UTC

[camel-karavan] branch main updated (2c0e3016 -> b85a3707)

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

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


    from 2c0e3016 Prototype actions #762
     new 712ab0a6 Fixing issues for prototype #757
     new b85a3707 Fixing issues for prototype #757

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


Summary of changes:
 karavan-app/pom.xml                                |  4 ++
 .../apache/camel/karavan/api/RunnerResource.java   | 30 +++++++++--
 .../camel/karavan/handler/PodEventHandler.java     |  4 +-
 .../camel/karavan/service/InfinispanService.java   | 44 +++++++++------
 .../camel/karavan/service/KubernetesService.java   | 62 ++++++++++++++++------
 .../camel/karavan/service/RunnerStatusService.java | 23 +++++---
 .../src/main/resources/application.properties      |  2 +
 karavan-app/src/main/webui/src/api/KaravanApi.tsx  |  9 ++++
 .../src/main/webui/src/projects/RunnerToolbar.tsx  |  6 +--
 .../camel/karavan/cli/resources/KaravanRole.java   |  6 +--
 karavan-runner/Dockerfile                          |  8 +--
 11 files changed, 145 insertions(+), 53 deletions(-)


[camel-karavan] 01/02: Fixing issues for prototype #757

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 712ab0a6d1727bd44b1559f6ad517570262f893f
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Wed May 10 20:03:21 2023 -0400

    Fixing issues for prototype #757
---
 karavan-app/pom.xml                                |  4 ++
 .../apache/camel/karavan/api/RunnerResource.java   | 15 ++++--
 .../camel/karavan/handler/PodEventHandler.java     |  2 +-
 .../camel/karavan/service/InfinispanService.java   | 23 ++++-----
 .../camel/karavan/service/KubernetesService.java   | 55 +++++++++++++++++-----
 .../camel/karavan/service/RunnerStatusService.java |  4 +-
 .../src/main/resources/application.properties      |  2 +
 karavan-app/src/main/webui/src/api/KaravanApi.tsx  |  9 ++++
 .../src/main/webui/src/projects/RunnerToolbar.tsx  |  6 +--
 .../camel/karavan/cli/resources/KaravanRole.java   |  6 +--
 karavan-runner/Dockerfile                          |  8 ++--
 11 files changed, 91 insertions(+), 43 deletions(-)

diff --git a/karavan-app/pom.xml b/karavan-app/pom.xml
index 255efbbd..654d668e 100644
--- a/karavan-app/pom.xml
+++ b/karavan-app/pom.xml
@@ -95,6 +95,10 @@
             <groupId>io.quarkus</groupId>
             <artifactId>quarkus-openshift-client</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-container-image-docker</artifactId>
+        </dependency>
         <dependency>
             <groupId>io.quarkus</groupId>
             <artifactId>quarkus-minikube</artifactId>
diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java
index 998e8246..90027649 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java
@@ -26,11 +26,7 @@ import javax.inject.Inject;
 import javax.ws.rs.*;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import java.util.List;
 import java.util.Optional;
-import java.util.stream.Collectors;
-
-import static org.apache.camel.karavan.service.KubernetesService.RUNNER_SUFFIX;
 
 @Path("/api/runner")
 public class RunnerResource {
@@ -49,7 +45,16 @@ public class RunnerResource {
     @Consumes(MediaType.APPLICATION_JSON)
     public String runProject(Project project) {
         Project p = infinispanService.getProject(project.getProjectId());
-        return kubernetesService.tryCreatePod(p);
+        return kubernetesService.tryCreateRunner(p);
+    }
+
+    @DELETE
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Path("/{name}")
+    public Response deletePod(@PathParam("name") String name) throws Exception {
+        kubernetesService.deleteRunner(name);
+        return Response.accepted().build();
     }
 
     @GET
diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java b/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java
index 9de06d30..8853655f 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java
@@ -64,7 +64,7 @@ public class PodEventHandler implements ResourceEventHandler<Pod> {
 
     public PodStatus getPodStatus(Pod pod) {
         String deployment = pod.getMetadata().getLabels().get("app");
-        String project = deployment != null ? deployment : pod.getMetadata().getLabels().get("project");
+        String project = deployment != null ? deployment : pod.getMetadata().getLabels().get("karavan/projectId");
         try {
             boolean initialized = pod.getStatus().getConditions().stream().anyMatch(c -> c.getType().equals("Initialized"));
             boolean ready = pod.getStatus().getConditions().stream().anyMatch(c -> c.getType().equals("Ready"));
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 b87efbd2..4c3dda1c 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,8 +16,6 @@
  */
 package org.apache.camel.karavan.service;
 
-import io.quarkus.runtime.LaunchMode;
-import io.quarkus.runtime.configuration.ProfileManager;
 import io.smallrye.mutiny.tuples.Tuple2;
 import org.apache.camel.karavan.model.CamelStatus;
 import org.apache.camel.karavan.model.DeploymentStatus;
@@ -39,6 +37,7 @@ import org.infinispan.commons.api.CacheContainerAdmin;
 import org.infinispan.commons.configuration.StringConfiguration;
 import org.infinispan.configuration.cache.CacheMode;
 import org.infinispan.configuration.cache.ConfigurationBuilder;
+import org.infinispan.configuration.cache.SingleFileStoreConfigurationBuilder;
 import org.infinispan.configuration.global.GlobalConfigurationBuilder;
 import org.infinispan.manager.DefaultCacheManager;
 import org.infinispan.query.dsl.QueryFactory;
@@ -91,18 +90,15 @@ public class InfinispanService implements HealthCheck  {
         if (cacheManager == null) {
             LOGGER.info("InfinispanService is starting in local mode");
             GlobalConfigurationBuilder global = GlobalConfigurationBuilder.defaultClusteredBuilder();
-            // TODO: Analyse if we need persistence for local cache.
-//            global.globalState().enable().persistentLocation("karavan-data");
+            global.globalState().enable().persistentLocation("/deployments/karavan-data");
             DefaultCacheManager cacheManager = new DefaultCacheManager(global.build());
             ConfigurationBuilder builder = new ConfigurationBuilder();
             builder.clustering()
                     .cacheMode(CacheMode.LOCAL)
-            // TODO: Analyse if we need persistence for local cache.
-//                    .persistence().passivation(false)
-//                    .addStore(SingleFileStoreConfigurationBuilder.class)
-//                    .shared(false)
-//                    .preload(true)
-            ;
+                    .persistence().passivation(false)
+                    .addStore(SingleFileStoreConfigurationBuilder.class)
+                    .shared(false)
+                    .preload(true);
             environments = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(Environment.CACHE, builder.build());
             projects = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(Project.CACHE, builder.build());
             files = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(ProjectFile.CACHE, builder.build());
@@ -125,6 +121,7 @@ public class InfinispanService implements HealthCheck  {
             camelStatuses = cacheManager.administration().getOrCreateCache(CamelStatus.CACHE, new StringConfiguration(String.format(CACHE_CONFIG, CamelStatus.CACHE)));
             commits = cacheManager.administration().getOrCreateCache("commits", new StringConfiguration(String.format(CACHE_CONFIG, "commits")));
         }
+        System.out.println("READY");
         ready.set(true);
     }
 
@@ -348,11 +345,11 @@ public class InfinispanService implements HealthCheck  {
 
     @Override
     public HealthCheckResponse call() {
-        if(ProfileManager.getLaunchMode() != LaunchMode.NORMAL && ready.get()){
+        if (cacheManager == null && ready.get()){
             return HealthCheckResponse.up("Infinispan Service is running in local mode.");
         }
-        else{
-            if(this.getRemoteCacheManager() != null && this.getRemoteCacheManager().isStarted() && ready.get()) {
+        else {
+            if (cacheManager != null && cacheManager.isStarted() && ready.get()) {
                 return HealthCheckResponse.up("Infinispan Service is running in cluster mode.");
             }
             else {
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 9874332b..006e5563 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
@@ -139,7 +139,7 @@ public class KubernetesService implements HealthCheck{
     @ConsumeEvent(value = STOP_INFORMERS, blocking = true)
     void stopInformers(String data) {
         LOGGER.info("Stop Kubernetes Informers");
-        informers.forEach(informer -> informer.close());
+        informers.forEach(SharedIndexInformer::close);
         informers.clear();
     }
 
@@ -382,23 +382,33 @@ public class KubernetesService implements HealthCheck{
         return result;
     }
 
-    public String tryCreatePod(Project project) {
+    public String tryCreateRunner(Project project) {
         String name = project.getProjectId() + "-" + RUNNER_SUFFIX;
-        createPVC(name + "-" + JBANG_CACHE_SUFFIX);
-        createPVC(name + "-" + M2_CACHE_SUFFIX);
+        createPVC(name + "-" + JBANG_CACHE_SUFFIX, name);
+        createPVC(name + "-" + M2_CACHE_SUFFIX, name);
         Pod old = kubernetesClient().pods().inNamespace(getNamespace()).withName(name).get();
         if (old == null) {
             ProjectFile properties = infinispanService.getProjectFile(project.getProjectId(), APPLICATION_PROPERTIES_FILENAME);
             Map<String,String> containerResources = ServiceUtil
                     .getRunnerContainerResourcesMap(properties, isOpenshift(), project.getRuntime().equals("quarkus"));
-            System.out.println(containerResources);
             Pod pod = getPod(project.getProjectId(), name, containerResources);
             Pod result = kubernetesClient().resource(pod).create();
             LOGGER.info("Created pod " + result.getMetadata().getName());
         }
+        createService(name);
         return name;
     }
 
+    public void deleteRunner(String name) {
+        try {
+            LOGGER.info("Delete runner: " + name + " in the namespace: " + getNamespace());
+            kubernetesClient().pods().inNamespace(getNamespace()).withName(name).delete();
+            kubernetesClient().services().inNamespace(getNamespace()).withName(name).delete();
+        } catch (Exception ex) {
+            LOGGER.error(ex.getMessage());
+        }
+    }
+
     public ResourceRequirements getResourceRequirements(Map<String,String> containerResources) {
         return new ResourceRequirementsBuilder()
                 .addToRequests("cpu", new Quantity(containerResources.get("requests.cpu")))
@@ -411,8 +421,8 @@ public class KubernetesService implements HealthCheck{
     private Pod getPod(String projectId, String name, Map<String,String> containerResources) {
         Map<String,String> labels = new HashMap<>();
         labels.putAll(getRuntimeLabels());
-        labels.putAll(getKaravanTypeLabel());
-        labels.put("project", projectId);
+        labels.putAll(getKaravanRunnerLabels(name));
+        labels.put("karavan/projectId", projectId);
 
         ResourceRequirements resources = getResourceRequirements(containerResources);
 
@@ -453,14 +463,14 @@ public class KubernetesService implements HealthCheck{
                 .build();
     }
 
-    private void createPVC(String pvcName) {
+    private void createPVC(String pvcName, String runnerName) {
         PersistentVolumeClaim old = kubernetesClient().persistentVolumeClaims().inNamespace(getNamespace()).withName(pvcName).get();
         if (old == null) {
             PersistentVolumeClaim pvc = new PersistentVolumeClaimBuilder()
                     .withNewMetadata()
                     .withName(pvcName)
                     .withNamespace(getNamespace())
-                    .withLabels(getKaravanTypeLabel())
+                    .withLabels(getKaravanRunnerLabels(runnerName))
                     .endMetadata()
                     .withNewSpec()
                     .withResources(new ResourceRequirementsBuilder().withRequests(Map.of("storage", new Quantity("2Gi"))).build())
@@ -468,10 +478,30 @@ public class KubernetesService implements HealthCheck{
                     .withAccessModes("ReadWriteOnce")
                     .endSpec()
                     .build();
-            kubernetesClient().resource(pvc).create();
+            kubernetesClient().resource(pvc).createOrReplace();
         }
     }
 
+    private void createService(String name) {
+
+        ServicePortBuilder portBuilder = new ServicePortBuilder()
+                .withName("http").withPort(80).withProtocol("TCP").withTargetPort(new IntOrString(8080));
+
+        Service service = new ServiceBuilder()
+                .withNewMetadata()
+                .withName(name)
+                .withNamespace(getNamespace())
+                .withLabels(getKaravanRunnerLabels(name))
+                .endMetadata()
+                .withNewSpec()
+                .withType("ClusterIP")
+                .withPorts(portBuilder.build())
+                .withSelector(getKaravanRunnerLabels(name))
+                .endSpec()
+                .build();
+        kubernetesClient().resource(service).createOrReplace();
+    }
+
     public Secret getKaravanSecret() {
         return kubernetesClient().secrets().inNamespace(getNamespace()).withName("karavan").get();
     }
@@ -492,8 +522,9 @@ public class KubernetesService implements HealthCheck{
         return map;
     }
 
-    public static Map<String, String> getKaravanTypeLabel() {
-        return Map.of("karavan/type" , "runner");
+    public static Map<String, String> getKaravanRunnerLabels(String name) {
+        return Map.of("karavan/type" , "runner",
+                "app.kubernetes.io/name", name);
     }
 
     public boolean isOpenshift() {
diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerStatusService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerStatusService.java
index 4ae496c9..e8c7bb1f 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerStatusService.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerStatusService.java
@@ -76,8 +76,8 @@ public class RunnerStatusService {
     @ConsumeEvent(value = CMD_COLLECT_RUNNER_STATUS, blocking = true, ordered = false)
     public void collectRunnerStatuses(String podName) throws ExecutionException, InterruptedException {
         String url = "http://" + podName + "." + kubernetesService.getNamespace() + ".svc.cluster.local/q/dev";
-//        HttpResponse<Buffer> result = bufferResult(url, 100);
-//        System.out.println(result);
+        HttpResponse<Buffer> result = bufferResult(url, 100);
+        System.out.println(result);
     }
 
     @CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay = 1000)
diff --git a/karavan-app/src/main/resources/application.properties b/karavan-app/src/main/resources/application.properties
index bfd804ca..37449a07 100644
--- a/karavan-app/src/main/resources/application.properties
+++ b/karavan-app/src/main/resources/application.properties
@@ -18,6 +18,7 @@ karavan.git-pull-interval=disabled
 quarkus.infinispan-client.devservices.enabled=false
 quarkus.infinispan-client.devservices.port=12345
 quarkus.infinispan-client.devservices.service-name=karavan
+quarkus.infinispan-client.health.enabled=false
 # Authentication
 quarkus.infinispan-client.username=admin
 quarkus.infinispan-client.password=password
@@ -87,6 +88,7 @@ quarkus.qute.strict-rendering=false
 quarkus.qute.property-not-found-strategy=output-original
 
 quarkus.container-image.builder=docker
+quarkus.health.extensions.enabled=false
 
 quarkus.kubernetes-client.connection-timeout=2000
 quarkus.kubernetes-client.request-timeout=10000
diff --git a/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-app/src/main/webui/src/api/KaravanApi.tsx
index 78736f22..99ea5dfa 100644
--- a/karavan-app/src/main/webui/src/api/KaravanApi.tsx
+++ b/karavan-app/src/main/webui/src/api/KaravanApi.tsx
@@ -318,6 +318,15 @@ export class KaravanApi {
         });
     }
 
+    static async deleteRunner(name: string, after: (res: AxiosResponse<any>) => void) {
+        instance.delete('/api/runner/' +  name)
+            .then(res => {
+                after(res);
+            }).catch(err => {
+            after(err);
+        });
+    }
+
     static async pipelineRun(project: Project, environment: string, after: (res: AxiosResponse<any>) => void) {
         instance.post('/api/kubernetes/pipeline/' + environment, project)
             .then(res => {
diff --git a/karavan-app/src/main/webui/src/projects/RunnerToolbar.tsx b/karavan-app/src/main/webui/src/projects/RunnerToolbar.tsx
index b2944ed7..532b91bb 100644
--- a/karavan-app/src/main/webui/src/projects/RunnerToolbar.tsx
+++ b/karavan-app/src/main/webui/src/projects/RunnerToolbar.tsx
@@ -39,9 +39,9 @@ export const RunnerToolbar = (props: Props) => {
         });
     }
 
-    function deletePod () {
+    function deleteRunner () {
         setIsDeletingPod(true);
-        KaravanApi.deletePod(props.config.environment, podName, res => {
+        KaravanApi.deleteRunner(podName, res => {
             if (res.status === 202) {
                 setIsDeletingPod(false);
             } else {
@@ -87,7 +87,7 @@ export const RunnerToolbar = (props: Props) => {
                                 variant={"secondary"}
                                 className="project-button"
                                 icon={!isRunning ? <DeleteIcon/> : <div></div>}
-                                onClick={() => deletePod()}>
+                                onClick={() => deleteRunner()}>
                             {isDeletingPod ? "..." : "Delete"}
                         </Button>
                     </Tooltip>
diff --git a/karavan-cli/src/main/java/org/apache/camel/karavan/cli/resources/KaravanRole.java b/karavan-cli/src/main/java/org/apache/camel/karavan/cli/resources/KaravanRole.java
index f2e14ba7..e93b72eb 100644
--- a/karavan-cli/src/main/java/org/apache/camel/karavan/cli/resources/KaravanRole.java
+++ b/karavan-cli/src/main/java/org/apache/camel/karavan/cli/resources/KaravanRole.java
@@ -29,11 +29,11 @@ public class KaravanRole {
                 .withNamespace(config.getNamespace())
                 .endMetadata()
                 .withRules(
-                        new PolicyRuleBuilder().withApiGroups("").withResources("secrets", "configmaps").withVerbs("get", "list").build(),
-                        new PolicyRuleBuilder().withApiGroups("").withResources("persistentvolumes", "persistentvolumeclaims").withVerbs("get", "list", "watch").build(),
+                        new PolicyRuleBuilder().withApiGroups("").withResources("secrets", "configmaps").withVerbs("*").build(),
+                        new PolicyRuleBuilder().withApiGroups("").withResources("persistentvolumes", "persistentvolumeclaims").withVerbs("*").build(),
                         new PolicyRuleBuilder().withApiGroups("tekton.dev").withResources("pipelineruns").withVerbs("*").build(),
                         new PolicyRuleBuilder().withApiGroups("").withResources("pods", "services", "replicationcontrollers").withVerbs("*").build(),
-                        new PolicyRuleBuilder().withApiGroups("route.openshift.io").withResources( "routes").withVerbs("*").build(),
+                        new PolicyRuleBuilder().withApiGroups("route.openshift.io").withResources("routes").withVerbs("*").build(),
                         new PolicyRuleBuilder().withApiGroups("apps").withResources("deployments").withVerbs("*").build()
                         )
                 .build();
diff --git a/karavan-runner/Dockerfile b/karavan-runner/Dockerfile
index 9d3febf7..8bc9f357 100644
--- a/karavan-runner/Dockerfile
+++ b/karavan-runner/Dockerfile
@@ -1,15 +1,15 @@
 FROM jbangdev/jbang-action:0.106.1
 
-ENV CAMEL_VERSION=3.20.4
+ENV CAMEL_VERSION=3.21.0-SNAPSHOT
 ENV KAMELETS_DIR="/kamelets"
 RUN mkdir /kamelets
 
 # Install Camel-JBang
 RUN jbang trust add -o --fresh --quiet https://github.com/apache/camel/blob/camel-$CAMEL_VERSION/dsl/camel-jbang/camel-jbang-main/dist/CamelJBang.java
-RUN jbang alias add --name camel https://github.com/apache/camel/blob/camel-$CAMEL_VERSION/dsl/camel-jbang/camel-jbang-main/dist/CamelJBang.java
+RUN jbang trust add -o --fresh --quiet https://github.com/apache/camel/blob/HEAD/dsl/camel-jbang/camel-jbang-main/dist/CamelJBang.java
 
 # Add demo routes
-COPY demo.camel.yaml /scripts/demo.camel.yaml
+# COPY demo.camel.yaml /scripts/demo.camel.yaml
 
 WORKDIR /scripts
-ENTRYPOINT jbang -Dcamel.jbang.version=$CAMEL_VERSION camel run * --console --local-kamelet-dir=$KAMELETS_DIR
\ No newline at end of file
+ENTRYPOINT jbang -Dcamel.jbang.version=$CAMEL_VERSION camel@apache/camel run --source-dir=/scripts --console --local-kamelet-dir=$KAMELETS_DIR
\ No newline at end of file


[camel-karavan] 02/02: Fixing issues for prototype #757

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit b85a3707765208b74246a76c498707f9801c3807
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Thu May 11 11:27:23 2023 -0400

    Fixing issues for prototype #757
---
 .../apache/camel/karavan/api/RunnerResource.java   | 19 ++++++++++++++++++
 .../camel/karavan/handler/PodEventHandler.java     |  2 +-
 .../camel/karavan/service/InfinispanService.java   | 21 ++++++++++++++++++--
 .../camel/karavan/service/KubernetesService.java   |  7 ++++---
 .../camel/karavan/service/RunnerStatusService.java | 23 ++++++++++++++--------
 5 files changed, 58 insertions(+), 14 deletions(-)

diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java
index 90027649..6aaeda5d 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java
@@ -16,18 +16,24 @@
  */
 package org.apache.camel.karavan.api;
 
+import io.vertx.core.json.JsonObject;
+import org.apache.camel.karavan.model.CamelStatus;
 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.apache.camel.karavan.service.StatusService;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 
 import javax.inject.Inject;
 import javax.ws.rs.*;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import java.util.Map;
 import java.util.Optional;
 
+import static org.apache.camel.karavan.service.KubernetesService.RUNNER_SUFFIX;
+
 @Path("/api/runner")
 public class RunnerResource {
 
@@ -70,4 +76,17 @@ public class RunnerResource {
             return Response.noContent().build();
         }
     }
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("/{projectId}")
+    public Response getCamelStatusByProjectAndEnv(@PathParam("projectId") String projectId) {
+        String name = projectId + "-" + RUNNER_SUFFIX;
+        String status = infinispanService.geRunnerStatus(name);
+        if (status != null) {
+            return Response.ok(status).build();
+        } else {
+            return Response.noContent().build();
+        }
+    }
 }
\ No newline at end of file
diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java b/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java
index 8853655f..a6ffb0e0 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java
@@ -50,7 +50,7 @@ public class PodEventHandler implements ResourceEventHandler<Pod> {
         try {
             LOGGER.info("onDelete " + pod.getMetadata().getName());
             String deployment = pod.getMetadata().getLabels().get("app");
-            String project = deployment != null ? deployment : pod.getMetadata().getLabels().get("project");
+            String project = deployment != null ? deployment : pod.getMetadata().getLabels().get("karavan/projectId");
             PodStatus ps = new PodStatus(
                     pod.getMetadata().getName(),
                     project,
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 4c3dda1c..48eb0a1f 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
@@ -71,6 +71,7 @@ public class InfinispanService implements HealthCheck  {
     private BasicCache<GroupedKey, ServiceStatus> serviceStatuses;
     private BasicCache<String, Environment> environments;
     private BasicCache<String, String> commits;
+    private BasicCache<String, String> runnerStatuses;
     private final AtomicBoolean ready = new AtomicBoolean(false);
 
     @Inject
@@ -108,6 +109,7 @@ public class InfinispanService implements HealthCheck  {
             serviceStatuses = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(ServiceStatus.CACHE, builder.build());
             camelStatuses = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(CamelStatus.CACHE, builder.build());
             commits = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache("commits", builder.build());
+            runnerStatuses = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache("runner_statuses", builder.build());
             cleanData();
         } else {
             LOGGER.info("InfinispanService is starting in remote mode");
@@ -120,6 +122,7 @@ public class InfinispanService implements HealthCheck  {
             serviceStatuses = cacheManager.administration().getOrCreateCache(ServiceStatus.CACHE, new StringConfiguration(String.format(CACHE_CONFIG, ServiceStatus.CACHE)));
             camelStatuses = cacheManager.administration().getOrCreateCache(CamelStatus.CACHE, new StringConfiguration(String.format(CACHE_CONFIG, CamelStatus.CACHE)));
             commits = cacheManager.administration().getOrCreateCache("commits", new StringConfiguration(String.format(CACHE_CONFIG, "commits")));
+            runnerStatuses = cacheManager.administration().getOrCreateCache("runner_statuses", new StringConfiguration(String.format(CACHE_CONFIG, "runner_statuses")));
         }
         System.out.println("READY");
         ready.set(true);
@@ -309,6 +312,7 @@ public class InfinispanService implements HealthCheck  {
                     .execute().list();
         }
     }
+
     public void saveCamelStatus(CamelStatus status) {
         camelStatuses.put(GroupedKey.create(status.getProjectId(), status.getEnv()), status);
     }
@@ -317,8 +321,20 @@ public class InfinispanService implements HealthCheck  {
         camelStatuses.remove(GroupedKey.create(name, env));
     }
 
+    public String geRunnerStatus(String podName) {
+        return runnerStatuses.get(podName);
+    }
+
+    public void saveRunnerStatus(String podName, String status) {
+        runnerStatuses.put(podName, status);
+    }
+
+    public void deleteRunnerStatus(String podName) {
+        runnerStatuses.remove(podName);
+    }
+
     public List<Environment> getEnvironments() {
-        return environments.values().stream().collect(Collectors.toList());
+        return new ArrayList<>(environments.values());
     }
 
     public void saveEnvironment(Environment environment) {
@@ -363,7 +379,8 @@ public class InfinispanService implements HealthCheck  {
             deploymentStatuses.clearAsync(),
             podStatuses.clearAsync(),
             pipelineStatuses.clearAsync(),
-            camelStatuses.clearAsync()
+            camelStatuses.clearAsync(),
+            runnerStatuses.clearAsync()
         ).join();
     }
 }
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 006e5563..9ddfde35 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
@@ -451,9 +451,10 @@ public class KubernetesService implements HealthCheck{
         PodSpec spec = new PodSpecBuilder()
                 .withTerminationGracePeriodSeconds(0L)
                 .withContainers(container)
-                .withVolumes(new VolumeBuilder().withName(name + "-" + JBANG_CACHE_SUFFIX)
-                        .withNewPersistentVolumeClaim(name + "-" + JBANG_CACHE_SUFFIX, false).build())
-                .withVolumes(new VolumeBuilder().withName(name + "-" + M2_CACHE_SUFFIX)
+                .withVolumes(
+                        new VolumeBuilder().withName(name + "-" + JBANG_CACHE_SUFFIX)
+                        .withNewPersistentVolumeClaim(name + "-" + JBANG_CACHE_SUFFIX, false).build(),
+                        new VolumeBuilder().withName(name + "-" + M2_CACHE_SUFFIX)
                         .withNewPersistentVolumeClaim(name + "-" + M2_CACHE_SUFFIX, false).build())
                 .build();
 
diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerStatusService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerStatusService.java
index e8c7bb1f..8ae0ecee 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerStatusService.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerStatusService.java
@@ -76,17 +76,24 @@ public class RunnerStatusService {
     @ConsumeEvent(value = CMD_COLLECT_RUNNER_STATUS, blocking = true, ordered = false)
     public void collectRunnerStatuses(String podName) throws ExecutionException, InterruptedException {
         String url = "http://" + podName + "." + kubernetesService.getNamespace() + ".svc.cluster.local/q/dev";
-        HttpResponse<Buffer> result = bufferResult(url, 100);
-        System.out.println(result);
+        String result = result(url, 100);
+        if (result != null) {
+            infinispanService.saveRunnerStatus(podName, result);
+        }
     }
 
     @CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay = 1000)
-    public HttpResponse<Buffer> bufferResult(String url, int timeout) throws InterruptedException, ExecutionException {
-        HttpResponse<Buffer> result = getWebClient().getAbs(url).putHeader("Accept", "application/json")
-                .timeout(timeout).send().subscribeAsCompletionStage().toCompletableFuture().get();
-        if (result.statusCode() == 200) {
-            JsonObject res = result.bodyAsJsonObject();
+    public String result(String url, int timeout) throws InterruptedException, ExecutionException {
+        try {
+            HttpResponse<Buffer> result = getWebClient().getAbs(url).putHeader("Accept", "application/json")
+                    .timeout(timeout).send().subscribeAsCompletionStage().toCompletableFuture().get();
+            if (result.statusCode() == 200) {
+                JsonObject res = result.bodyAsJsonObject();
+                return res.encodePrettily();
+            }
+        } catch (Exception e) {
+            LOGGER.info(e.getMessage());
         }
-        return result;
+        return null;
     }
 }
\ No newline at end of file