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 1c4c46ab Exchange tracing #757
1c4c46ab is described below
commit 1c4c46abdabcb877068387a31970ae3e6626cc5c
Author: Marat Gubaidullin <[email protected]>
AuthorDate: Sat May 13 21:16:27 2023 -0400
Exchange tracing #757
---
.../apache/camel/karavan/api/LogWatchResource.java | 1 +
.../apache/camel/karavan/api/RunnerResource.java | 15 +++-
.../apache/camel/karavan/model/RunnerStatus.java | 3 +-
.../camel/karavan/service/InfinispanService.java | 1 -
.../camel/karavan/service/KubernetesService.java | 5 +-
.../camel/karavan/service/ProjectService.java | 1 -
.../camel/karavan/service/RunnerService.java | 40 +++++++++-
karavan-app/src/main/webui/src/api/KaravanApi.tsx | 13 ++-
karavan-app/src/main/webui/src/index.css | 7 ++
.../main/webui/src/projects/CreateFileModal.tsx | 2 +-
.../main/webui/src/projects/ProjectDevelopment.tsx | 69 +++++++++++-----
.../src/main/webui/src/projects/ProjectEventBus.ts | 17 ++++
.../src/main/webui/src/projects/ProjectLog.tsx | 6 +-
.../main/webui/src/projects/RunnerInfoContext.tsx | 22 +++---
.../webui/src/projects/RunnerInfoDataModal.tsx | 82 +++++++++++++++++++
.../main/webui/src/projects/RunnerInfoMemory.tsx | 2 -
.../src/main/webui/src/projects/RunnerInfoPod.tsx | 2 +-
.../main/webui/src/projects/RunnerInfoTrace.tsx | 92 ++++++++++++++++++++++
.../src/main/webui/src/projects/RunnerToolbar.tsx | 84 ++++++++++++++------
19 files changed, 393 insertions(+), 71 deletions(-)
diff --git
a/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java
b/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java
index e4e5efff..b0cade62 100644
---
a/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java
+++
b/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java
@@ -76,6 +76,7 @@ public class LogWatchResource {
LOGGER.error(e.getMessage());
}
logWatch.close();
+ sink.close();
LOGGER.info("LogWatch for " + name + " closed");
}
});
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 f07df5e2..eb169a48 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
@@ -62,18 +62,25 @@ public class RunnerResource {
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, runnerName);
}
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("/reload/{projectId}")
+ public Response reload(@PathParam("projectId") String projectId) {
+ runnerServices.reload(projectId);
+ return Response.ok().build();
+ }
+
@DELETE
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
- @Path("/{name}")
- public Response deletePod(@PathParam("name") String name) {
- kubernetesService.deleteRunner(name);
+ @Path("/{name}/{deletePVC}")
+ public Response deleteRunner(@PathParam("name") String name,
@PathParam("deletePVC") boolean deletePVC) {
+ kubernetesService.deleteRunner(name, deletePVC);
infinispanService.deleteRunnerStatuses(name);
return Response.accepted().build();
}
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
index de474bd9..f1928a20 100644
--- 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
@@ -9,6 +9,7 @@ public class RunnerStatus {
properties,
route,
trace,
- jvm
+ jvm,
+ source
}
}
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 b1fffc1f..b09a0831 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
@@ -127,7 +127,6 @@ public class InfinispanService implements HealthCheck {
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);
}
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 77ddaed5..0224e4e4 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
@@ -397,11 +397,14 @@ public class KubernetesService implements HealthCheck{
return runnerName;
}
- public void deleteRunner(String name) {
+ public void deleteRunner(String name, boolean deletePVC) {
try {
LOGGER.info("Delete runner: " + name + " in the namespace: " +
getNamespace());
kubernetesClient().pods().inNamespace(getNamespace()).withName(name).delete();
kubernetesClient().services().inNamespace(getNamespace()).withName(name).delete();
+ if (deletePVC) {
+
kubernetesClient().persistentVolumeClaims().inNamespace(getNamespace()).withName(name).delete();
+ }
} catch (Exception ex) {
LOGGER.error(ex.getMessage());
}
diff --git
a/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java
b/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java
index 85e511ca..f3f7bd7e 100644
---
a/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java
+++
b/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java
@@ -77,7 +77,6 @@ public class ProjectService implements HealthCheck{
LOGGER.info("Pull commits...");
Tuple2<String, Integer> lastCommit =
infinispanService.getLastCommit();
gitService.getCommitsAfterCommit(lastCommit.getItem2()).forEach(commitInfo -> {
- System.out.println(commitInfo);
if (!infinispanService.hasCommit(commitInfo.getCommitId())) {
commitInfo.getRepos().forEach(repo -> {
Project project = importProjectFromRepo(repo);
diff --git
a/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerService.java
b/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerService.java
index 709da51c..2bb70cf7 100644
---
a/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerService.java
+++
b/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerService.java
@@ -25,6 +25,7 @@ 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.ProjectFile;
import org.apache.camel.karavan.model.RunnerStatus;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
@@ -32,7 +33,9 @@ import org.jboss.logging.Logger;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
@@ -67,6 +70,39 @@ public class RunnerService {
return webClient;
}
+ public void reload(String projectId) {
+ try {
+ String runnerName = projectId + "-" + RUNNER_SUFFIX;
+ infinispanService.getProjectFiles(projectId).forEach(projectFile
-> putRequest(runnerName, projectFile.getName(), projectFile.getCode(), 1000));
+ reloadRequest(runnerName);
+ } catch (Exception ex) {
+ LOGGER.error(ex.getMessage());
+ }
+ }
+
+ @CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay =
1000)
+ public boolean putRequest(String runnerName, String fileName, String body,
int timeout) {
+ try {
+ String url = "http://" + runnerName + "." +
kubernetesService.getNamespace() + ".svc.cluster.local/q/upload/" + fileName;
+ HttpResponse<Buffer> result = getWebClient().putAbs(url)
+
.timeout(timeout).sendBuffer(Buffer.buffer(body)).subscribeAsCompletionStage().toCompletableFuture().get();
+ return result.statusCode() == 200;
+ } catch (Exception e) {
+ LOGGER.info(e.getMessage());
+ }
+ return false;
+ }
+
+ public String reloadRequest(String runnerName) {
+ String url = "http://" + runnerName + "." +
kubernetesService.getNamespace() +
".svc.cluster.local/q/dev/reload?reload=true";
+ try {
+ return result(url, 1000);
+ } catch (InterruptedException | ExecutionException e) {
+ LOGGER.error(e.getMessage());
+ }
+ return null;
+ }
+
@Scheduled(every = "{karavan.runner-status-interval}", concurrentExecution
= Scheduled.ConcurrentExecution.SKIP)
void collectRunnerStatus() {
if (infinispanService.call().getStatus().name().equals("UP")) {
@@ -86,7 +122,9 @@ public class RunnerService {
.filter(name -> !name.equals(RunnerStatus.NAME.context))
.forEach(statusName -> {
String status = getRunnerStatus(podName, statusName);
- infinispanService.saveRunnerStatus(podName,
statusName, status);
+ if (status != null) {
+ infinispanService.saveRunnerStatus(podName,
statusName, status);
+ }
});
reloadCode(podName, oldContext, newContext);
}
diff --git a/karavan-app/src/main/webui/src/api/KaravanApi.tsx
b/karavan-app/src/main/webui/src/api/KaravanApi.tsx
index 45b86a2a..7142c571 100644
--- a/karavan-app/src/main/webui/src/api/KaravanApi.tsx
+++ b/karavan-app/src/main/webui/src/api/KaravanApi.tsx
@@ -309,6 +309,15 @@ export class KaravanApi {
});
}
+ static async getRunnerReload(projectId: string, after: (res:
AxiosResponse<any>) => void) {
+ instance.get('/api/runner/reload/' + projectId)
+ .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 => {
@@ -327,8 +336,8 @@ export class KaravanApi {
});
}
- static async deleteRunner(name: string, after: (res: AxiosResponse<any>)
=> void) {
- instance.delete('/api/runner/' + name)
+ static async deleteRunner(name: string, deletePVC: boolean, after: (res:
AxiosResponse<any>) => void) {
+ instance.delete('/api/runner/' + name + "/" + deletePVC)
.then(res => {
after(res);
}).catch(err => {
diff --git a/karavan-app/src/main/webui/src/index.css
b/karavan-app/src/main/webui/src/index.css
index 7fa80c96..e3525ba9 100644
--- a/karavan-app/src/main/webui/src/index.css
+++ b/karavan-app/src/main/webui/src/index.css
@@ -161,6 +161,13 @@
height: 30px;
}
+.karavan .project-page .project-development .pf-c-panel__header {
+ padding: 0;
+}
+.karavan .project-page .project-development .pf-c-panel__main {
+ max-height: 212px;
+}
+
.karavan .project-page .project-status {
margin-bottom: 16px;
}
diff --git a/karavan-app/src/main/webui/src/projects/CreateFileModal.tsx
b/karavan-app/src/main/webui/src/projects/CreateFileModal.tsx
index 90183a0f..0bbcd836 100644
--- a/karavan-app/src/main/webui/src/projects/CreateFileModal.tsx
+++ b/karavan-app/src/main/webui/src/projects/CreateFileModal.tsx
@@ -5,7 +5,7 @@ import {
FormGroup,
ModalVariant,
Form,
- ToggleGroupItem, ToggleGroup, TextInputGroupMain, TextInputGroupUtilities,
TextInputGroup, Text, FormHelperText, HelperText, HelperTextItem, TextInput
+ ToggleGroupItem, ToggleGroup, FormHelperText, HelperText, HelperTextItem,
TextInput
} from '@patternfly/react-core';
import '../designer/karavan.css';
import {KaravanApi} from "../api/KaravanApi";
diff --git a/karavan-app/src/main/webui/src/projects/ProjectDevelopment.tsx
b/karavan-app/src/main/webui/src/projects/ProjectDevelopment.tsx
index 64f16999..1e19cc6d 100644
--- a/karavan-app/src/main/webui/src/projects/ProjectDevelopment.tsx
+++ b/karavan-app/src/main/webui/src/projects/ProjectDevelopment.tsx
@@ -1,7 +1,7 @@
import React, {useEffect, useRef, useState} from 'react';
import {
Card,
- CardBody, Flex, FlexItem, Divider
+ CardBody, Flex, FlexItem, Divider, Tab, Tabs, CardFooter
} from '@patternfly/react-core';
import '../designer/karavan.css';
import {PodStatus, Project} from "./ProjectModels";
@@ -11,6 +11,7 @@ import {RunnerInfoContext} from "./RunnerInfoContext";
import {RunnerInfoMemory} from "./RunnerInfoMemory";
import {KaravanApi} from "../api/KaravanApi";
import {ProjectEventBus} from "./ProjectEventBus";
+import {RunnerInfoTrace} from "./RunnerInfoTrace";
export function isRunning(status: PodStatus): boolean {
return status.phase === 'Running' && !status.terminating;
@@ -29,14 +30,28 @@ export const ProjectDevelopment = (props: Props) => {
const [memory, setMemory] = useState({});
const [jvm, setJvm] = useState({});
const [context, setContext] = useState({});
+ const [trace, setTrace] = useState({});
+ const [showTrace, setShowTrace] = useState(false);
+ const [refreshTrace, setRefreshTrace] = useState(true);
useEffect(() => {
previousValue.current = podStatus;
+ const sub1 = ProjectEventBus.onShowTrace()?.subscribe((result) => {
+ setShowTrace(result.show);
+ });
+ const sub2 = ProjectEventBus.onRefreshTrace()?.subscribe((result) => {
+ setRefreshTrace(result);
+ });
const interval = setInterval(() => {
onRefreshStatus();
}, 1000);
- return () => clearInterval(interval);
+ return () => {
+ sub1.unsubscribe();
+ sub2.unsubscribe();
+ clearInterval(interval)
+ };
+
}, [podStatus]);
function onRefreshStatus() {
@@ -74,35 +89,49 @@ export const ProjectDevelopment = (props: Props) => {
setContext({});
}
})
+ if (refreshTrace) {
+ KaravanApi.getRunnerConsoleStatus(projectId, "trace", res => {
+ if (res.status === 200) {
+ setTrace(res.data);
+ } else {
+ setTrace({});
+ }
+ })
+ }
}
function showConsole(): boolean {
- return podStatus.phase !== '' ;
+ return podStatus.phase !== '';
}
const {project, config} = props;
return (
- <Card className="project-development">
- <CardBody>
- <Flex direction={{default: "row"}}
- justifyContent={{default:
"justifyContentSpaceBetween"}}>
- <FlexItem flex={{default: "flex_1"}}>
- <RunnerInfoPod podStatus={podStatus}
config={config} showConsole={showConsole()} />
- </FlexItem>
+ <Card className="project-development">
+ <CardBody>
+ <Flex direction={{default: "row"}}
+ justifyContent={{default: "justifyContentSpaceBetween"}}>
+ {!showTrace && <FlexItem flex={{default: "flex_1"}}>
+ <RunnerInfoPod podStatus={podStatus} config={config}
showConsole={showConsole()}/>
+ </FlexItem>}
+ {showConsole() && !showTrace && <>
<Divider orientation={{default: "vertical"}}/>
<FlexItem flex={{default: "flex_1"}}>
- <RunnerInfoMemory jvm={jvm} memory={memory}
config={config} showConsole={showConsole()} />
+ <RunnerInfoMemory jvm={jvm} memory={memory}
config={config} showConsole={showConsole()}/>
</FlexItem>
<Divider orientation={{default: "vertical"}}/>
<FlexItem flex={{default: "flex_1"}}>
- <RunnerInfoContext context={context}
config={config} showConsole={showConsole()} />
- </FlexItem>
- <Divider orientation={{default: "vertical"}}/>
- <FlexItem>
- <RunnerToolbar project={project} config={config}
showConsole={showConsole()} />
+ <RunnerInfoContext context={context}
config={config} showConsole={showConsole()}/>
</FlexItem>
- </Flex>
- </CardBody>
- </Card>
- )
+ </>}
+ {showConsole() && showTrace && <FlexItem flex={{default:
"flex_1"}} style={{margin:"0"}}>
+ <RunnerInfoTrace trace={trace}
refreshTrace={refreshTrace}/>
+ </FlexItem>}
+ <Divider orientation={{default: "vertical"}}/>
+ <FlexItem>
+ <RunnerToolbar project={project} config={config}
showConsole={showConsole()}/>
+ </FlexItem>
+ </Flex>
+ </CardBody>
+ </Card>
+ )
}
diff --git a/karavan-app/src/main/webui/src/projects/ProjectEventBus.ts
b/karavan-app/src/main/webui/src/projects/ProjectEventBus.ts
index 93535629..f2d8397c 100644
--- a/karavan-app/src/main/webui/src/projects/ProjectEventBus.ts
+++ b/karavan-app/src/main/webui/src/projects/ProjectEventBus.ts
@@ -20,6 +20,8 @@ import {Project} from "./ProjectModels";
const currentProject = new Subject<Project>();
const currentFile = new Subject<string>();
const showLog = new Subject<ShowLogCommand>();
+const showTrace = new Subject<ShowTraceCommand>();
+const refreshTrace = new Subject<boolean>();
export class ShowLogCommand {
type: 'container' | 'pipeline'
@@ -35,6 +37,15 @@ export class ShowLogCommand {
}
}
+export class ShowTraceCommand {
+ name: string
+ show: boolean
+
+ constructor(name: string, show: boolean) {
+ this.name = name;
+ this.show = show;
+ }
+}
export const ProjectEventBus = {
selectProject: (project: Project) => currentProject.next(project),
@@ -46,4 +57,10 @@ export const ProjectEventBus = {
showLog: (type: 'container' | 'pipeline', name: string, environment:
string, show: boolean = true) =>
showLog.next(new ShowLogCommand(type, name, environment, show)),
onShowLog: () => showLog.asObservable(),
+
+ showTrace: (name: string, show: boolean = true) => showTrace.next(new
ShowTraceCommand(name, show)),
+ onShowTrace: () => showTrace.asObservable(),
+
+ refreshTrace: (refresh: boolean) => refreshTrace.next(refresh),
+ onRefreshTrace: () => refreshTrace.asObservable(),
}
diff --git a/karavan-app/src/main/webui/src/projects/ProjectLog.tsx
b/karavan-app/src/main/webui/src/projects/ProjectLog.tsx
index 82b5fb23..829c58c7 100644
--- a/karavan-app/src/main/webui/src/projects/ProjectLog.tsx
+++ b/karavan-app/src/main/webui/src/projects/ProjectLog.tsx
@@ -62,8 +62,10 @@ export class ProjectLog extends React.Component<Props,
State> {
this.eventSource?.close();
}
this.eventSource.onmessage = (event) => {
- const data = this.state.data.concat('\n').concat(event.data)
- this.setState({data: data, currentLine: this.state.currentLine +
1});
+ this.setState((state: Readonly<State>) => {
+ const data = state.data.concat('\n').concat(event.data)
+ return {data: data, currentLine: this.state.currentLine + 1}
+ });
};
}
diff --git a/karavan-app/src/main/webui/src/projects/RunnerInfoContext.tsx
b/karavan-app/src/main/webui/src/projects/RunnerInfoContext.tsx
index 8f49c15b..79624695 100644
--- a/karavan-app/src/main/webui/src/projects/RunnerInfoContext.tsx
+++ b/karavan-app/src/main/webui/src/projects/RunnerInfoContext.tsx
@@ -56,14 +56,14 @@ export const RunnerInfoContext = (props: Props) => {
{props.context?.context?.state}
</Label>
</Tooltip>
- <Tooltip content="Uptime" position={"bottom"}>
+ <Tooltip content="Phase" position={"bottom"}>
<Label icon={getIcon()} color={getColor()}>
- {props.context?.context?.uptime}
+ {props.context?.context?.phase}
</Label>
</Tooltip>
- <Tooltip content="Phase" position={"bottom"}>
+ <Tooltip content="Uptime" position={"bottom"}>
<Label icon={getIcon()} color={getColor()}>
- {props.context?.context?.phase}
+ {props.context?.context?.uptime}
</Label>
</Tooltip>
</LabelGroup>
@@ -138,13 +138,12 @@ export const RunnerInfoContext = (props: Props) => {
return (
<DescriptionList isHorizontal>
- <DescriptionListGroup>
- <DescriptionListTerm>Camel</DescriptionListTerm>
- <DescriptionListDescription>
- {getContextInfo()}
- </DescriptionListDescription>
- </DescriptionListGroup>
- {props.showConsole && <>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Camel</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getContextInfo()}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>Version</DescriptionListTerm>
<DescriptionListDescription>
@@ -169,7 +168,6 @@ export const RunnerInfoContext = (props: Props) => {
{getProcessingTime()}
</DescriptionListDescription>
</DescriptionListGroup>
- </>}
</DescriptionList>
);
}
diff --git a/karavan-app/src/main/webui/src/projects/RunnerInfoDataModal.tsx
b/karavan-app/src/main/webui/src/projects/RunnerInfoDataModal.tsx
new file mode 100644
index 00000000..f8700833
--- /dev/null
+++ b/karavan-app/src/main/webui/src/projects/RunnerInfoDataModal.tsx
@@ -0,0 +1,82 @@
+import React, {useEffect, useRef, useState} from 'react';
+import {
+ Badge, Bullseye,
+ Button, CodeBlock, CodeBlockCode, DataList, DataListCell, DataListItem,
DataListItemCells, DataListItemRow,
+ DescriptionList,
+ DescriptionListDescription,
+ DescriptionListGroup,
+ DescriptionListTerm, Divider, EmptyState, EmptyStateIcon,
EmptyStateVariant, Form, FormGroup, FormHelperText, HelperText, HelperTextItem,
+ Label, LabelGroup, Modal, ModalVariant, Panel, PanelHeader, PanelMain,
PanelMainBody, Switch, Text, TextInput, Title, ToggleGroup, ToggleGroupItem,
+ Tooltip, TooltipPosition
+} from '@patternfly/react-core';
+import '../designer/karavan.css';
+import {getProjectFileType, PodStatus, Project, ProjectFileTypes} 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";
+import {TableComposable, Tbody, Td, Th, Thead, Tr} from
"@patternfly/react-table";
+import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
+import SearchIcon from "@patternfly/react-icons/dist/esm/icons/search-icon";
+
+interface Props {
+ trace: any
+ isOpen: boolean
+ onClose: () => void
+}
+
+export const RunnerInfoDataModal = (props: Props) => {
+
+ const type = props.trace?.message?.body?.type;
+ const body = props.trace?.message?.body?.value;
+ const headers: any[] = [{key: "header1", value: "value1"}, {key:
"header2", value: "value2"}];
+ return (
+ <Modal
+ title={"Exchange: " + props.trace?.message?.exchangeId}
+ variant={ModalVariant.large}
+ isOpen={props.isOpen}
+ onClose={() => props.onClose?.call(this)}
+ actions={[
+ <Button key="cancel" variant="primary" onClick={event =>
props.onClose?.call(this)}>Close</Button>
+ ]}
+ >
+ <Panel isScrollable>
+ <PanelMain tabIndex={0}>
+ <PanelHeader>
+ <DescriptionList isHorizontal>
+ <DescriptionListGroup>
+
<DescriptionListTerm>Headers</DescriptionListTerm>
+ <DescriptionListDescription>
+ <DataList aria-label="Compact data list
example" isCompact>
+ {headers.map((header: any) => (
+ <DataListItem key={header[0]}
aria-labelledby="compact-item1">
+ <DataListItemRow>
+ <DataListItemCells
+ dataListCells={[
+ <DataListCell
key="uid">{header.key}</DataListCell>,
+ <DataListCell
key="routeId">{header.value}</DataListCell>,
+ ]}
+ />
+ </DataListItemRow>
+ </DataListItem>))}
+ </DataList>
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Body</DescriptionListTerm>
+ <DescriptionListDescription>
+ {type && <Label>{type}</Label>}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ </DescriptionList>
+ </PanelHeader>
+ <PanelMainBody style={{padding: "0"}}>
+ <CodeBlock title="Body">
+ <CodeBlockCode
id="code-content">{body}</CodeBlockCode>
+ </CodeBlock>
+ </PanelMainBody>
+ </PanelMain>
+ </Panel>
+ </Modal>
+ );
+}
diff --git a/karavan-app/src/main/webui/src/projects/RunnerInfoMemory.tsx
b/karavan-app/src/main/webui/src/projects/RunnerInfoMemory.tsx
index 2511b79d..e90af48a 100644
--- a/karavan-app/src/main/webui/src/projects/RunnerInfoMemory.tsx
+++ b/karavan-app/src/main/webui/src/projects/RunnerInfoMemory.tsx
@@ -128,7 +128,6 @@ export const RunnerInfoMemory = (props: Props) => {
{getJvmInfo()}
</DescriptionListDescription>
</DescriptionListGroup>
- {props.showConsole && <>
<DescriptionListGroup>
<DescriptionListTerm>PID</DescriptionListTerm>
<DescriptionListDescription>
@@ -153,7 +152,6 @@ export const RunnerInfoMemory = (props: Props) => {
{getNonHeapInfo()}
</DescriptionListDescription>
</DescriptionListGroup>
- </>}
</DescriptionList>
);
}
diff --git a/karavan-app/src/main/webui/src/projects/RunnerInfoPod.tsx
b/karavan-app/src/main/webui/src/projects/RunnerInfoPod.tsx
index be606cd3..3e05256a 100644
--- a/karavan-app/src/main/webui/src/projects/RunnerInfoPod.tsx
+++ b/karavan-app/src/main/webui/src/projects/RunnerInfoPod.tsx
@@ -28,7 +28,7 @@ export const RunnerInfoPod = (props: Props) => {
const podStatus = props.podStatus;
return (
<Label icon={getIcon()} color={getColor()}>
- <Tooltip content={`Phase: ${JSON.stringify(podStatus)}`}>
+ <Tooltip content={"Show log"}>
<Button variant="link"
onClick={e => ProjectEventBus.showLog('container',
podStatus.name, env)}>
{podStatus.name}
diff --git a/karavan-app/src/main/webui/src/projects/RunnerInfoTrace.tsx
b/karavan-app/src/main/webui/src/projects/RunnerInfoTrace.tsx
new file mode 100644
index 00000000..044c0345
--- /dev/null
+++ b/karavan-app/src/main/webui/src/projects/RunnerInfoTrace.tsx
@@ -0,0 +1,92 @@
+import React, {useEffect, useRef, useState} from 'react';
+import {
+ Badge, Bullseye,
+ Button, DataList, DataListCell, DataListItem, DataListItemCells,
DataListItemRow,
+ DescriptionList,
+ DescriptionListDescription,
+ DescriptionListGroup,
+ DescriptionListTerm, Divider, EmptyState, EmptyStateIcon,
EmptyStateVariant,
+ Label, LabelGroup, Panel, PanelHeader, PanelMain, PanelMainBody, Switch,
Title,
+ Tooltip, TooltipPosition
+} from '@patternfly/react-core';
+import '../designer/karavan.css';
+import {getProjectFileType, 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";
+import {TableComposable, Tbody, Td, Th, Thead, Tr} from
"@patternfly/react-table";
+import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
+import SearchIcon from "@patternfly/react-icons/dist/esm/icons/search-icon";
+import {RunnerInfoDataModal} from "./RunnerInfoDataModal";
+
+
+interface Props {
+ trace: any
+ refreshTrace: boolean
+}
+
+export const RunnerInfoTrace = (props: Props) => {
+
+ const [trace, setTrace] = useState({});
+ const [isOpen, setIsOpen] = useState(false);
+
+ function closeModal() {
+ setIsOpen(false);
+ }
+
+ const traces: any[] = props.trace?.trace?.traces || [];
+ return (
+ <Panel isScrollable>
+ <RunnerInfoDataModal isOpen={isOpen} trace={trace}
onClose={closeModal}/>
+ <PanelHeader>
+ <div style={{display:"flex", flexDirection:"row",
justifyContent:"space-between"}}>
+ <DescriptionList>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Trace routed
messages</DescriptionListTerm>
+ </DescriptionListGroup>
+ </DescriptionList>
+ <div style={{marginRight: "16px"}}>
+ <Tooltip content="Auto refresh"
position={TooltipPosition.left}>
+ <Switch aria-label="refresh"
+ id="refresh"
+ isChecked={props.refreshTrace}
+ onChange={checked =>
ProjectEventBus.refreshTrace(checked)}
+ />
+ </Tooltip>
+ </div>
+ </div>
+ </PanelHeader>
+ <PanelMain tabIndex={0}>
+ <PanelMainBody style={{padding: "0"}}>
+ <DataList aria-label="Compact data list example" isCompact>
+ {traces.filter(t => t.nodeId === undefined)
+ .sort((a, b) => b.uid > a.uid ? 1 : -1)
+ .map(trace => (
+ <DataListItem key={trace.uid}
aria-labelledby="compact-item1">
+ <DataListItemRow>
+ <DataListItemCells
+ dataListCells={[
+ <DataListCell
key="uid">{trace.uid}</DataListCell>,
+ <DataListCell
key="routeId">{trace.routeId}</DataListCell>,
+ <DataListCell key="exchangeId">
+ <Button style={{padding:
'0'}} variant={"link"}
+ onClick={e => {
+
setTrace(trace);
+
setIsOpen(true);
+ }}>
+
{trace.message.exchangeId}
+ </Button>
+
+ </DataListCell>,
+ <DataListCell
key="timestamp">{new Date(trace.timestamp).toISOString()}</DataListCell>
+ ]}
+ />
+ </DataListItemRow>
+ </DataListItem>))}
+ </DataList>
+ </PanelMainBody>
+ </PanelMain>
+ </Panel>
+ );
+}
diff --git a/karavan-app/src/main/webui/src/projects/RunnerToolbar.tsx
b/karavan-app/src/main/webui/src/projects/RunnerToolbar.tsx
index cd972fe3..f552aa33 100644
--- a/karavan-app/src/main/webui/src/projects/RunnerToolbar.tsx
+++ b/karavan-app/src/main/webui/src/projects/RunnerToolbar.tsx
@@ -1,13 +1,14 @@
import React, {useState} from 'react';
import {
- Button,
+ Button, Label, Switch, Tab, Tabs,
Tooltip,
TooltipPosition
} from '@patternfly/react-core';
import '../designer/karavan.css';
import {Project} from "./ProjectModels";
import RocketIcon from "@patternfly/react-icons/dist/esm/icons/rocket-icon";
-import PlayIcon from "@patternfly/react-icons/dist/esm/icons/play-icon";
+import ReloadIcon from "@patternfly/react-icons/dist/esm/icons/bolt-icon";
+import TraceIcon from "@patternfly/react-icons/dist/esm/icons/list-icon";
import DeleteIcon from
"@patternfly/react-icons/dist/esm/icons/times-circle-icon";
import {KaravanApi} from "../api/KaravanApi";
import {ProjectEventBus} from "./ProjectEventBus";
@@ -25,6 +26,8 @@ export const RunnerToolbar = (props: Props) => {
const [isJbangRunning, setJbangIsRunning] = useState(false);
const [isRunning, setIsRunning] = useState(false);
const [isDeletingPod, setIsDeletingPod] = useState(false);
+ const [isReloadingPod, setIsReloadingPod] = useState(false);
+ const [isShowingTrace, setIsShowingTrace] = useState(false);
function jbangRun() {
setJbangIsRunning(true);
@@ -40,9 +43,21 @@ export const RunnerToolbar = (props: Props) => {
});
}
+ function reloadRunner() {
+ setIsReloadingPod(true);
+ KaravanApi.getRunnerReload(props.project.projectId, res => {
+ if (res.status === 200 || res.status === 201) {
+ setIsReloadingPod(false);
+ } else {
+ // Todo notification
+ setIsReloadingPod(false);
+ }
+ });
+ }
+
function deleteRunner() {
setIsDeletingPod(true);
- KaravanApi.deleteRunner(podName, res => {
+ KaravanApi.deleteRunner(podName, false, res => {
if (res.status === 202) {
setIsDeletingPod(false);
} else {
@@ -52,36 +67,61 @@ export const RunnerToolbar = (props: Props) => {
});
}
+ function showTrace() {
+ ProjectEventBus.showTrace(props.project.projectId, !isShowingTrace);
+ setIsShowingTrace((prevState) => !prevState);
+ }
+
return (
- <React.Fragment>
<div className="runner-toolbar">
- <div className="row">
- <Tooltip content="Run in development mode"
position={TooltipPosition.left}>
- <Button isLoading={isJbangRunning ? true : undefined}
- isSmall
- variant={"primary"}
- className="project-button"
- icon={!isJbangRunning ? <RocketIcon/> :
<div></div>}
- onClick={() => jbangRun()}>
- {isJbangRunning ? "..." : "Run"}
- </Button>
- </Tooltip>
- </div>
+ {!props.showConsole &&
+ <div className="row">
+ <Tooltip content="Run in development mode"
position={TooltipPosition.left}>
+ <Button isLoading={isJbangRunning ? true :
undefined}
+ isSmall
+ variant={"primary"}
+ className="project-button"
+ icon={!isJbangRunning ? <RocketIcon/> :
<div></div>}
+ onClick={() => jbangRun()}>
+ {isJbangRunning ? "..." : "Run"}
+ </Button>
+ </Tooltip>
+ </div>}
{props.showConsole && <>
<div className="row">
- <Tooltip content="Delete pod"
position={TooltipPosition.left}>
- <Button isLoading={isDeletingPod ? true :
undefined}
+ <Tooltip content="Reload"
position={TooltipPosition.left}>
+ <Button isLoading={isReloadingPod ? true :
undefined}
isSmall
+ variant={"primary"}
+ className="project-button"
+ icon={!isReloadingPod ? <ReloadIcon/> :
<div></div>}
+ onClick={() => reloadRunner()}>
+ {isReloadingPod ? "..." : "Reload"}
+ </Button>
+ </Tooltip>
+ </div>
+ <div className="row">
+ <Tooltip content="Show trace"
position={TooltipPosition.left}>
+ <Button isSmall
variant={"secondary"}
className="project-button"
- icon={!isRunning ? <DeleteIcon/> :
<div></div>}
- onClick={() => deleteRunner()}>
- {isDeletingPod ? "..." : "Delete"}
+ icon={ <TraceIcon/>}
+ onClick={() => showTrace()}>
+ {isShowingTrace ? "Runtime" : "Trace"}
</Button>
</Tooltip>
</div>
+ <Tooltip content="Delete runner"
position={TooltipPosition.left}>
+ <Button isLoading={isDeletingPod ? true : undefined}
+ isSmall
+ variant={"secondary"}
+ className="project-button"
+ icon={!isRunning ? <DeleteIcon/> : <div></div>}
+ onClick={() => deleteRunner()}>
+ {isDeletingPod ? "..." : "Delete"}
+ </Button>
+ </Tooltip>
</>}
</div>
- </React.Fragment>
);
}