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
The following commit(s) were added to refs/heads/main by this push:
new e5b51352 Dev console JVM and Camel Contex #757
e5b51352 is described below
commit e5b513524d362947df2351164b56ba2db2492226
Author: Marat Gubaidullin <[email protected]>
AuthorDate: Fri May 12 12:54:25 2023 -0400
Dev console JVM and Camel Contex #757
---
.../apache/camel/karavan/api/RunnerResource.java | 37 ++--
.../camel/karavan/handler/PodEventHandler.java | 9 +-
.../apache/camel/karavan/model/RunnerStatus.java | 14 ++
.../camel/karavan/service/InfinispanService.java | 27 ++-
.../camel/karavan/service/KubernetesService.java | 17 +-
...RunnerStatusService.java => RunnerService.java} | 63 ++++++-
karavan-app/src/main/webui/src/api/KaravanApi.tsx | 11 +-
.../main/webui/src/projects/ProjectDevelopment.tsx | 8 +-
.../main/webui/src/projects/RunnerInfoContext.tsx | 192 +++++++++++++++++++++
.../main/webui/src/projects/RunnerInfoMemory.tsx | 183 ++++++++++++++++++++
10 files changed, 517 insertions(+), 44 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 6aaeda5d..f07df5e2 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
@@ -17,22 +17,27 @@
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.model.RunnerStatus;
import org.apache.camel.karavan.service.InfinispanService;
import org.apache.camel.karavan.service.KubernetesService;
-import org.apache.camel.karavan.service.StatusService;
+import org.apache.camel.karavan.service.RunnerService;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import javax.inject.Inject;
-import javax.ws.rs.*;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
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;
+import static org.apache.camel.karavan.service.RunnerService.RUNNER_SUFFIX;
@Path("/api/runner")
public class RunnerResource {
@@ -40,6 +45,9 @@ public class RunnerResource {
@ConfigProperty(name = "karavan.environment")
String environment;
+ @Inject
+ RunnerService runnerServices;
+
@Inject
KubernetesService kubernetesService;
@@ -50,22 +58,29 @@ public class RunnerResource {
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public String runProject(Project project) {
+ String runnerName = project.getProjectId() + "-" + RUNNER_SUFFIX;
+ String status = infinispanService.getRunnerStatus(runnerName,
RunnerStatus.NAME.context);
+ if (status != null) {
+ JsonObject js = new JsonObject(status);
+ System.out.println(status);
+ }
Project p = infinispanService.getProject(project.getProjectId());
- return kubernetesService.tryCreateRunner(p);
+ return kubernetesService.tryCreateRunner(p, runnerName);
}
@DELETE
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/{name}")
- public Response deletePod(@PathParam("name") String name) throws Exception
{
+ public Response deletePod(@PathParam("name") String name) {
kubernetesService.deleteRunner(name);
+ infinispanService.deleteRunnerStatuses(name);
return Response.accepted().build();
}
@GET
@Produces(MediaType.APPLICATION_JSON)
- @Path("/status/{projectId}/{name}")
+ @Path("/pod/{projectId}/{name}")
public Response getPodStatus(@PathParam("projectId") String projectId,
@PathParam("name") String name) {
Optional<PodStatus> ps = infinispanService.getPodStatuses(projectId,
environment).stream()
.filter(podStatus -> podStatus.getName().equals(name))
@@ -79,10 +94,10 @@ public class RunnerResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
- @Path("/{projectId}")
- public Response getCamelStatusByProjectAndEnv(@PathParam("projectId")
String projectId) {
+ @Path("/console/{statusName}/{projectId}")
+ public Response getCamelStatusByProjectAndEnv(@PathParam("projectId")
String projectId, @PathParam("statusName") String statusName) {
String name = projectId + "-" + RUNNER_SUFFIX;
- String status = infinispanService.geRunnerStatus(name);
+ String status = infinispanService.getRunnerStatus(name,
RunnerStatus.NAME.valueOf(statusName));
if (status != null) {
return Response.ok(status).build();
} else {
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 a6ffb0e0..508c753d 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
@@ -1,15 +1,16 @@
package org.apache.camel.karavan.handler;
-import io.fabric8.kubernetes.api.model.*;
+import io.fabric8.kubernetes.api.model.ContainerBuilder;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.Quantity;
+import io.fabric8.kubernetes.api.model.ResourceRequirements;
import io.fabric8.kubernetes.client.informers.ResourceEventHandler;
import org.apache.camel.karavan.model.PodStatus;
import org.apache.camel.karavan.service.InfinispanService;
import org.apache.camel.karavan.service.KubernetesService;
import org.jboss.logging.Logger;
-import java.util.Optional;
-
-import static org.apache.camel.karavan.service.KubernetesService.RUNNER_SUFFIX;
+import static org.apache.camel.karavan.service.RunnerService.RUNNER_SUFFIX;
import static
org.apache.camel.karavan.service.ServiceUtil.DEFAULT_CONTAINER_RESOURCES;
public class PodEventHandler implements ResourceEventHandler<Pod> {
diff --git
a/karavan-app/src/main/java/org/apache/camel/karavan/model/RunnerStatus.java
b/karavan-app/src/main/java/org/apache/camel/karavan/model/RunnerStatus.java
new file mode 100644
index 00000000..de474bd9
--- /dev/null
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/RunnerStatus.java
@@ -0,0 +1,14 @@
+package org.apache.camel.karavan.model;
+
+public class RunnerStatus {
+
+ public enum NAME {
+ context,
+ inflight,
+ memory,
+ properties,
+ route,
+ trace,
+ jvm
+ }
+}
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 48eb0a1f..b1fffc1f 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
@@ -25,6 +25,7 @@ import org.apache.camel.karavan.model.PipelineStatus;
import org.apache.camel.karavan.model.PodStatus;
import org.apache.camel.karavan.model.Project;
import org.apache.camel.karavan.model.ProjectFile;
+import org.apache.camel.karavan.model.RunnerStatus;
import org.apache.camel.karavan.model.ServiceStatus;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
@@ -48,6 +49,7 @@ import javax.enterprise.inject.Default;
import javax.inject.Inject;
import java.time.Instant;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -71,7 +73,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 BasicCache<GroupedKey, String> runnerStatuses;
private final AtomicBoolean ready = new AtomicBoolean(false);
@Inject
@@ -91,7 +93,7 @@ public class InfinispanService implements HealthCheck {
if (cacheManager == null) {
LOGGER.info("InfinispanService is starting in local mode");
GlobalConfigurationBuilder global =
GlobalConfigurationBuilder.defaultClusteredBuilder();
-
global.globalState().enable().persistentLocation("/deployments/karavan-data");
+ global.globalState().enable().persistentLocation("karavan-data");
DefaultCacheManager cacheManager = new
DefaultCacheManager(global.build());
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.clustering()
@@ -99,7 +101,8 @@ public class InfinispanService implements HealthCheck {
.persistence().passivation(false)
.addStore(SingleFileStoreConfigurationBuilder.class)
.shared(false)
- .preload(true);
+ .preload(true)
+ .fetchPersistentState(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());
@@ -321,16 +324,22 @@ public class InfinispanService implements HealthCheck {
camelStatuses.remove(GroupedKey.create(name, env));
}
- public String geRunnerStatus(String podName) {
- return runnerStatuses.get(podName);
+ public String getRunnerStatus(String podName, RunnerStatus.NAME
statusName) {
+ return runnerStatuses.get(GroupedKey.create(podName,
statusName.name()));
}
- public void saveRunnerStatus(String podName, String status) {
- runnerStatuses.put(podName, status);
+ public void saveRunnerStatus(String podName, RunnerStatus.NAME statusName,
String status) {
+ runnerStatuses.put(GroupedKey.create(podName, statusName.name()),
status);
}
- public void deleteRunnerStatus(String podName) {
- runnerStatuses.remove(podName);
+ public void deleteRunnerStatus(String podName, RunnerStatus.NAME
statusName) {
+ runnerStatuses.remove(GroupedKey.create(podName, statusName.name()));
+ }
+
+ public void deleteRunnerStatuses(String podName) {
+ Arrays.stream(RunnerStatus.NAME.values()).forEach(statusName -> {
+ runnerStatuses.remove(GroupedKey.create(podName,
statusName.name()));
+ });
}
public List<Environment> getEnvironments() {
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 034f3e1d..d24b776e 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
@@ -59,7 +59,6 @@ public class KubernetesService implements HealthCheck{
public static final int INFORMERS = 4;
private static final String CAMEL_PREFIX = "camel";
private static final String KARAVAN_PREFIX = "karavan";
- public static final String RUNNER_SUFFIX = "runner";
private static final String JBANG_CACHE_SUFFIX = "jbang-cache";
private static final String M2_CACHE_SUFFIX = "m2-cache";
@@ -382,21 +381,21 @@ public class KubernetesService implements HealthCheck{
return result;
}
- public String tryCreateRunner(Project project) {
- String name = project.getProjectId() + "-" + RUNNER_SUFFIX;
- createPVC(name + "-" + JBANG_CACHE_SUFFIX, name);
- createPVC(name + "-" + M2_CACHE_SUFFIX, name);
- Pod old =
kubernetesClient().pods().inNamespace(getNamespace()).withName(name).get();
+ public String tryCreateRunner(Project project, String runnerName) {
+
+ createPVC(runnerName + "-" + JBANG_CACHE_SUFFIX, runnerName);
+ createPVC(runnerName + "-" + M2_CACHE_SUFFIX, runnerName);
+ Pod old =
kubernetesClient().pods().inNamespace(getNamespace()).withName(runnerName).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"));
- Pod pod = getPod(project.getProjectId(), name, containerResources);
+ Pod pod = getPod(project.getProjectId(), runnerName,
containerResources);
Pod result = kubernetesClient().resource(pod).create();
LOGGER.info("Created pod " + result.getMetadata().getName());
}
- createService(name);
- return name;
+ createService(runnerName);
+ return runnerName;
}
public void deleteRunner(String name) {
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/RunnerService.java
similarity index 59%
rename from
karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerStatusService.java
rename to
karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerService.java
index 8ae0ecee..709da51c 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/RunnerService.java
@@ -25,20 +25,23 @@ import io.vertx.mutiny.core.eventbus.EventBus;
import io.vertx.mutiny.ext.web.client.HttpResponse;
import io.vertx.mutiny.ext.web.client.WebClient;
import org.apache.camel.karavan.model.PodStatus;
+import org.apache.camel.karavan.model.RunnerStatus;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
-import org.eclipse.microprofile.faulttolerance.Retry;
import org.jboss.logging.Logger;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
+import java.util.Arrays;
+import java.util.Objects;
import java.util.concurrent.ExecutionException;
@ApplicationScoped
-public class RunnerStatusService {
+public class RunnerService {
- private static final Logger LOGGER =
Logger.getLogger(RunnerStatusService.class.getName());
+ private static final Logger LOGGER =
Logger.getLogger(RunnerService.class.getName());
public static final String CMD_COLLECT_RUNNER_STATUS =
"collect-runner-status";
+ public static final String RUNNER_SUFFIX = "runner";
@Inject
InfinispanService infinispanService;
@@ -69,19 +72,61 @@ public class RunnerStatusService {
if (infinispanService.call().getStatus().name().equals("UP")) {
infinispanService.getPodStatuses(environment).stream().filter(PodStatus::getRunner).forEach(podStatus
-> {
eventBus.publish(CMD_COLLECT_RUNNER_STATUS,
podStatus.getName());
- });
+ });
}
}
@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";
- String result = result(url, 100);
- if (result != null) {
- infinispanService.saveRunnerStatus(podName, result);
+ public void collectRunnerStatuses(String podName) {
+ String oldContext = infinispanService.getRunnerStatus(podName,
RunnerStatus.NAME.context);
+ String newContext = getRunnerStatus(podName,
RunnerStatus.NAME.context);
+ if (newContext != null) {
+ infinispanService.saveRunnerStatus(podName,
RunnerStatus.NAME.context, newContext);
+ Arrays.stream(RunnerStatus.NAME.values())
+ .filter(name -> !name.equals(RunnerStatus.NAME.context))
+ .forEach(statusName -> {
+ String status = getRunnerStatus(podName, statusName);
+ infinispanService.saveRunnerStatus(podName,
statusName, status);
+ });
+ reloadCode(podName, oldContext, newContext);
}
}
+ private void reloadCode(String podName, String oldContext, String
newContext) {
+ String projectName = podName.replace("-" + RUNNER_SUFFIX, "");
+ String newState = getContextState(newContext);
+ String oldState = getContextState(oldContext);
+ if (newContext != null && !Objects.equals(newState, oldState) &&
"Running".equals(newState)) {
+ sendCodeToRunner(projectName);
+ }
+ }
+
+ private void sendCodeToRunner(String projectName) {
+ infinispanService.getProjectFiles(projectName).forEach(projectFile -> {
+
+ });
+ }
+
+ private String getContextState(String context) {
+ if (context != null) {
+ JsonObject obj = new JsonObject(context);
+ return obj.getJsonObject("context").getString("state");
+ } else {
+ return null;
+ }
+ }
+
+ public String getRunnerStatus(String podName, RunnerStatus.NAME
statusName) {
+ String url = "http://" + podName + "." +
kubernetesService.getNamespace() + ".svc.cluster.local/q/dev/" +
statusName.name();
+// String url = "http://0.0.0.0:8888/q/dev/" + statusName.name();
+ try {
+ return result(url, 1000);
+ } catch (InterruptedException | ExecutionException e) {
+ LOGGER.error(e.getMessage());
+ }
+ return null;
+ }
+
@CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay =
1000)
public String result(String url, int timeout) throws InterruptedException,
ExecutionException {
try {
diff --git a/karavan-app/src/main/webui/src/api/KaravanApi.tsx
b/karavan-app/src/main/webui/src/api/KaravanApi.tsx
index 99ea5dfa..45b86a2a 100644
--- a/karavan-app/src/main/webui/src/api/KaravanApi.tsx
+++ b/karavan-app/src/main/webui/src/api/KaravanApi.tsx
@@ -301,7 +301,16 @@ export class KaravanApi {
}
static async getRunnerPodStatus(projectId: string, name: string, after:
(res: AxiosResponse<PodStatus>) => void) {
- instance.get('/api/runner/status/' + projectId + "/" + name)
+ instance.get('/api/runner/pod/' + projectId + "/" + name)
+ .then(res => {
+ after(res);
+ }).catch(err => {
+ after(err);
+ });
+ }
+
+ static async getRunnerConsoleStatus(projectId: string, statusName: string,
after: (res: AxiosResponse<string>) => void) {
+ instance.get('/api/runner/console/' + statusName + "/" + projectId)
.then(res => {
after(res);
}).catch(err => {
diff --git a/karavan-app/src/main/webui/src/projects/ProjectDevelopment.tsx
b/karavan-app/src/main/webui/src/projects/ProjectDevelopment.tsx
index ce2fc1c7..e065b315 100644
--- a/karavan-app/src/main/webui/src/projects/ProjectDevelopment.tsx
+++ b/karavan-app/src/main/webui/src/projects/ProjectDevelopment.tsx
@@ -7,6 +7,8 @@ import '../designer/karavan.css';
import {Project} from "./ProjectModels";
import {RunnerToolbar} from "./RunnerToolbar";
import {RunnerInfoPod} from "./RunnerInfoPod";
+import {RunnerInfoContext} from "./RunnerInfoContext";
+import {RunnerInfoMemory} from "./RunnerInfoMemory";
interface Props {
@@ -27,7 +29,11 @@ export const ProjectDevelopment = (props: Props) => {
</FlexItem>
<Divider orientation={{default: "vertical"}}/>
<FlexItem flex={{default: "flex_1"}}>
- {/*<Runner project={project} config={config} />*/}
+ <RunnerInfoMemory project={project}
config={config} />
+ </FlexItem>
+ <Divider orientation={{default: "vertical"}}/>
+ <FlexItem flex={{default: "flex_1"}}>
+ <RunnerInfoContext project={project}
config={config} />
</FlexItem>
<Divider orientation={{default: "vertical"}}/>
<FlexItem>
diff --git a/karavan-app/src/main/webui/src/projects/RunnerInfoContext.tsx
b/karavan-app/src/main/webui/src/projects/RunnerInfoContext.tsx
new file mode 100644
index 00000000..9bd7e333
--- /dev/null
+++ b/karavan-app/src/main/webui/src/projects/RunnerInfoContext.tsx
@@ -0,0 +1,192 @@
+import React, {useEffect, useRef, useState} from 'react';
+import {
+ Button,
+ DescriptionList,
+ DescriptionListDescription,
+ DescriptionListGroup,
+ DescriptionListTerm,
+ Label, LabelGroup,
+ Tooltip
+} from '@patternfly/react-core';
+import '../designer/karavan.css';
+import {PodStatus, Project} from "./ProjectModels";
+import {KaravanApi} from "../api/KaravanApi";
+import {ProjectEventBus} from "./ProjectEventBus";
+import DownIcon from
"@patternfly/react-icons/dist/esm/icons/error-circle-o-icon";
+import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
+
+
+interface Props {
+ project: Project,
+ config: any,
+}
+
+export const RunnerInfoContext = (props: Props) => {
+
+ const [context, setContext] = useState({});
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ onRefreshStatus();
+ }, 1000);
+ return () => clearInterval(interval);
+ }, []);
+
+ function onRefreshStatus() {
+ const projectId = props.project.projectId;
+ KaravanApi.getRunnerConsoleStatus(projectId, "context", res => {
+ if (res.status === 200) {
+ setContext(res.data);
+ } else {
+ setContext({});
+ }
+ })
+ }
+
+ function getContextInfo() {
+ return (
+ <LabelGroup numLabels={3}>
+ <Tooltip content="Name" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(context as any)?.context?.name}
+ </Label>
+ </Tooltip>
+ </LabelGroup>
+ )
+ }
+
+ function getVersionInfo() {
+ return (
+ <LabelGroup numLabels={3}>
+ <Tooltip content="Version" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(context as any)?.context?.version}
+ </Label>
+ </Tooltip>
+ </LabelGroup>
+ )
+ }
+
+ function getContextState() {
+ return (
+ <LabelGroup numLabels={3}>
+ <Tooltip content="State" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(context as any)?.context?.state}
+ </Label>
+ </Tooltip>
+ <Tooltip content="Uptime" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(context as any)?.context?.uptime}
+ </Label>
+ </Tooltip>
+ <Tooltip content="Phase" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(context as any)?.context?.phase}
+ </Label>
+ </Tooltip>
+ </LabelGroup>
+ )
+ }
+
+ function getExchanges() {
+ return (
+ <LabelGroup numLabels={3}>
+ <Tooltip content="Total" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(context as any)?.context?.statistics?.exchangesTotal}
+ </Label>
+ </Tooltip>
+ <Tooltip content="Failed" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(context as
any)?.context?.statistics?.exchangesFailed}
+ </Label>
+ </Tooltip>
+ <Tooltip content="Inflight" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(context as
any)?.context?.statistics?.exchangesInflight}
+ </Label>
+ </Tooltip>
+ </LabelGroup>
+ )
+ }
+
+ function getProcessingTime() {
+ return (
+ <LabelGroup numLabels={4}>
+ <Tooltip content="Min" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(context as
any)?.context?.statistics?.minProcessingTime}
+ </Label>
+ </Tooltip>
+ <Tooltip content="Mean" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(context as
any)?.context?.statistics?.meanProcessingTime}
+ </Label>
+ </Tooltip>
+ <Tooltip content="Max" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(context as
any)?.context?.statistics?.maxProcessingTime}
+ </Label>
+ </Tooltip>
+ <Tooltip content="Last" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(context as
any)?.context?.statistics?.lastProcessingTime}
+ </Label>
+ </Tooltip>
+ </LabelGroup>
+ )
+ }
+
+ function getIcon() {
+ return (getRunning() ? <UpIcon/> : <DownIcon/>)
+ }
+
+ function getColor() {
+ return getRunning() ? "green" : "grey";
+ }
+
+ function getRunning(): boolean {
+ return isRunning(context);
+ }
+
+
+ function isRunning(c: any): boolean {
+ return c?.context?.state === 'Started';
+ }
+
+ return (
+ <DescriptionList isHorizontal>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Camel Context</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getContextInfo()}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Version</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getVersionInfo()}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ <DescriptionListGroup>
+ <DescriptionListTerm>State</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getContextState()}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Exchanges:</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getExchanges()}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Processing Time</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getProcessingTime()}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ </DescriptionList>
+ );
+}
diff --git a/karavan-app/src/main/webui/src/projects/RunnerInfoMemory.tsx
b/karavan-app/src/main/webui/src/projects/RunnerInfoMemory.tsx
new file mode 100644
index 00000000..9558dc35
--- /dev/null
+++ b/karavan-app/src/main/webui/src/projects/RunnerInfoMemory.tsx
@@ -0,0 +1,183 @@
+import React, {useEffect, useRef, useState} from 'react';
+import {
+ Button,
+ DescriptionList,
+ DescriptionListDescription,
+ DescriptionListGroup,
+ DescriptionListTerm,
+ Label, LabelGroup,
+ Tooltip
+} from '@patternfly/react-core';
+import '../designer/karavan.css';
+import {PodStatus, Project} from "./ProjectModels";
+import {KaravanApi} from "../api/KaravanApi";
+import {ProjectEventBus} from "./ProjectEventBus";
+import DownIcon from
"@patternfly/react-icons/dist/esm/icons/error-circle-o-icon";
+import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
+
+
+interface Props {
+ project: Project,
+ config: any,
+}
+
+export const RunnerInfoMemory = (props: Props) => {
+
+ const [memory, setMemory] = useState({});
+ const [jvm, setJvm] = useState({});
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ onRefreshStatus();
+ }, 1000);
+ return () => clearInterval(interval);
+ }, []);
+
+ function onRefreshStatus() {
+ const projectId = props.project.projectId;
+ KaravanApi.getRunnerConsoleStatus(projectId, "memory", res => {
+ if (res.status === 200) {
+ setMemory(res.data);
+ } else {
+ setMemory({});
+ }
+ })
+ KaravanApi.getRunnerConsoleStatus(projectId, "jvm", res => {
+ if (res.status === 200) {
+ setJvm(res.data);
+ } else {
+ setJvm({});
+ }
+ })
+ }
+
+ function getJvmInfo() {
+ return (
+ <LabelGroup numLabels={2}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(jvm as any)?.jvm?.vmVendor} {(jvm as
any)?.jvm?.vmVersion}
+ </Label>
+ </LabelGroup>
+ )
+ }
+
+ function getHeapInfo() {
+ return (
+ <LabelGroup numLabels={3}>
+ <Tooltip content="Init" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(memory as any)?.memory?.heapMemoryInit}
+ </Label>
+ </Tooltip>
+ <Tooltip content="Max" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(memory as any)?.memory?.heapMemoryMax}
+ </Label>
+ </Tooltip>
+ <Tooltip content="Used" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(memory as any)?.memory?.heapMemoryUsed}
+ </Label>
+ </Tooltip>
+ </LabelGroup>
+ )
+ }
+
+ function getJvmUptime() {
+ return (
+ <LabelGroup numLabels={2}>
+ <Tooltip content="Uptime" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(jvm as any)?.jvm?.vmUptime}
+ </Label>
+ </Tooltip>
+ </LabelGroup>
+ )
+ }
+
+ function getPid() {
+ return (
+ <LabelGroup numLabels={2}>
+ <Tooltip content="PID" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(jvm as any)?.jvm?.pid}
+ </Label>
+ </Tooltip>
+ </LabelGroup>
+ )
+ }
+
+ function getNonHeapInfo() {
+ return (
+ <LabelGroup numLabels={3}>
+ <Tooltip content="Init" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(memory as any)?.memory?.nonHeapMemoryInit}
+ </Label>
+ </Tooltip>
+ <Tooltip content="Max" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(memory as any)?.memory?.nonHeapMemoryMax}
+ </Label>
+ </Tooltip>
+ <Tooltip content="Used" position={"bottom"}>
+ <Label icon={getIcon()} color={getColor()}>
+ {(memory as any)?.memory?.nonHeapMemoryUsed}
+ </Label>
+ </Tooltip>
+ </LabelGroup>
+ )
+ }
+
+ function getIcon() {
+ return (getRunning() ? <UpIcon/> : <DownIcon/>)
+ }
+
+ function getColor() {
+ return getRunning() ? "green" : "grey";
+ }
+
+ function getRunning(): boolean {
+ return isRunning(jvm);
+ }
+
+
+ function isRunning(c: any): boolean {
+ return c?.jvm?.pid != undefined;
+ }
+
+ return (
+ <DescriptionList isHorizontal>
+ <DescriptionListGroup>
+ <DescriptionListTerm>JVM Memory</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getJvmInfo()}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ <DescriptionListGroup>
+ <DescriptionListTerm>PID</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getPid()}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Uptime</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getJvmUptime()}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Heap</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getHeapInfo()}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Non-Heap</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getNonHeapInfo()}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ </DescriptionList>
+ );
+}