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 8e0fd273 Collect container port information from Docker
8e0fd273 is described below
commit 8e0fd2730ee3c11521cc9196dd48d050f6cb51a6
Author: Marat Gubaidullin <[email protected]>
AuthorDate: Mon Nov 20 15:28:19 2023 -0500
Collect container port information from Docker
---
.../camel/karavan/docker/DockerServiceUtils.java | 6 +-
.../karavan/infinispan/model/ContainerPort.java | 62 +++++++++
.../karavan/infinispan/model/ContainerStatus.java | 14 +--
.../karavan/infinispan/model/KaravanSchema.java | 1 +
.../src/main/webui/src/api/ProjectModels.ts | 8 +-
.../webui/src/containers/ContainerTableRow.tsx | 30 +++--
.../knowledgebase/components/ComponentModal.tsx | 138 +++++++++++++++------
.../main/webui/src/services/ServicesTableRow.tsx | 19 ++-
8 files changed, 220 insertions(+), 58 deletions(-)
diff --git
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerServiceUtils.java
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerServiceUtils.java
index f436b5a8..79cb561d 100644
---
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerServiceUtils.java
+++
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerServiceUtils.java
@@ -18,9 +18,11 @@ package org.apache.camel.karavan.docker;
import com.github.dockerjava.api.model.*;
import io.smallrye.mutiny.tuples.Tuple2;
+import io.vertx.core.json.JsonArray;
import org.apache.camel.karavan.api.KameletResources;
import org.apache.camel.karavan.code.model.DockerComposeHealthCheck;
import org.apache.camel.karavan.infinispan.model.ContainerStatus;
+import org.apache.camel.karavan.infinispan.model.ContainerPort;
import java.io.BufferedReader;
import java.io.InputStream;
@@ -44,7 +46,9 @@ public class DockerServiceUtils {
protected ContainerStatus getContainerStatus(Container container, String
environment) {
String name = container.getNames()[0].replace("/", "");
- List<Integer> ports =
Arrays.stream(container.getPorts()).map(ContainerPort::getPrivatePort).filter(Objects::nonNull).collect(Collectors.toList());
+ List<ContainerPort> ports = Arrays.stream(container.getPorts())
+ .map(p -> new ContainerPort(p.getPrivatePort(),
p.getPublicPort(), p.getType()))
+ .collect(Collectors.toList());
List<ContainerStatus.Command> commands =
getContainerCommand(container.getState());
ContainerStatus.ContainerType type =
getContainerType(container.getLabels());
String created =
Instant.ofEpochSecond(container.getCreated()).toString();
diff --git
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerPort.java
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerPort.java
new file mode 100644
index 00000000..af8cebf9
--- /dev/null
+++
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerPort.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.camel.karavan.infinispan.model;
+
+import org.infinispan.protostream.annotations.ProtoFactory;
+import org.infinispan.protostream.annotations.ProtoField;
+
+public class ContainerPort {
+
+ @ProtoField(number = 1)
+ Integer privatePort;
+ @ProtoField(number = 2)
+ Integer publicPort;
+ @ProtoField(number = 3)
+ String type;
+
+ @ProtoFactory
+ public ContainerPort(Integer privatePort, Integer publicPort, String type)
{
+ this.privatePort = privatePort;
+ this.publicPort = publicPort;
+ this.type = type;
+ }
+
+ public Integer getPrivatePort() {
+ return privatePort;
+ }
+
+ public void setPrivatePort(Integer privatePort) {
+ this.privatePort = privatePort;
+ }
+
+ public Integer getPublicPort() {
+ return publicPort;
+ }
+
+ public void setPublicPort(Integer publicPort) {
+ this.publicPort = publicPort;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+}
\ No newline at end of file
diff --git
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java
index b6a4394d..441d78d1 100644
---
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java
+++
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java
@@ -62,7 +62,7 @@ public class ContainerStatus {
@ProtoField(number = 4)
String image;
@ProtoField(number = 5, collectionImplementation = ArrayList.class)
- List<Integer> ports;
+ List<ContainerPort> ports;
@ProtoField(number = 6)
String env;
@ProtoField(number = 7)
@@ -93,7 +93,7 @@ public class ContainerStatus {
String camelRuntime;
@ProtoFactory
- public ContainerStatus(String projectId, String containerName, String
containerId, String image, List<Integer> ports, String env, ContainerType type,
String memoryInfo, String cpuInfo, String created, String finished,
List<Command> commands, String state, String phase, Boolean codeLoaded, Boolean
inTransit, String initDate, String podIP, String camelRuntime) {
+ public ContainerStatus(String projectId, String containerName, String
containerId, String image, List<ContainerPort> ports, String env, ContainerType
type, String memoryInfo, String cpuInfo, String created, String finished,
List<Command> commands, String state, String phase, Boolean codeLoaded, Boolean
inTransit, String initDate, String podIP, String camelRuntime) {
this.projectId = projectId;
this.containerName = containerName;
this.containerId = containerId;
@@ -115,7 +115,7 @@ public class ContainerStatus {
this.camelRuntime = camelRuntime;
}
- public ContainerStatus(String projectId, String containerName, String
containerId, String image, List<Integer> ports, String env, ContainerType type,
String memoryInfo, String cpuInfo, String created, String finished,
List<Command> commands, String state, String phase, Boolean codeLoaded, Boolean
inTransit, String initDate) {
+ public ContainerStatus(String projectId, String containerName, String
containerId, String image, List<ContainerPort> ports, String env, ContainerType
type, String memoryInfo, String cpuInfo, String created, String finished,
List<Command> commands, String state, String phase, Boolean codeLoaded, Boolean
inTransit, String initDate) {
this.projectId = projectId;
this.containerName = containerName;
this.containerId = containerId;
@@ -135,7 +135,7 @@ public class ContainerStatus {
this.initDate = initDate;
}
- public ContainerStatus(String projectId, String containerName, String
containerId, String image, List<Integer> ports, String env, ContainerType type,
String memoryInfo, String cpuInfo, String created, String finished,
List<Command> commands, String state, Boolean codeLoaded, Boolean inTransit,
String camelRuntime) {
+ public ContainerStatus(String projectId, String containerName, String
containerId, String image, List<ContainerPort> ports, String env, ContainerType
type, String memoryInfo, String cpuInfo, String created, String finished,
List<Command> commands, String state, Boolean codeLoaded, Boolean inTransit,
String camelRuntime) {
this.projectId = projectId;
this.containerName = containerName;
this.containerId = containerId;
@@ -185,7 +185,7 @@ public class ContainerStatus {
return new ContainerStatus(name, name, null, null, null, env, type,
null, null, null, null, List.of(Command.run), null, false, false, "");
}
- public static ContainerStatus createWithId(String projectId, String
containerName, String env, String containerId, String image, List<Integer>
ports, ContainerType type, List<Command> commands, String status, String
created, String camelRuntime) {
+ public static ContainerStatus createWithId(String projectId, String
containerName, String env, String containerId, String image,
List<ContainerPort> ports, ContainerType type, List<Command> commands, String
status, String created, String camelRuntime) {
return new ContainerStatus(projectId, containerName, containerId,
image, ports, env, type,
null, null, created, null, commands, status, false, false,
camelRuntime);
}
@@ -233,11 +233,11 @@ public class ContainerStatus {
this.image = image;
}
- public List<Integer> getPorts() {
+ public List<ContainerPort> getPorts() {
return ports;
}
- public void setPorts(List<Integer> ports) {
+ public void setPorts(List<ContainerPort> ports) {
this.ports = ports;
}
diff --git
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/KaravanSchema.java
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/KaravanSchema.java
index f5e2d063..79d5eea3 100644
---
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/KaravanSchema.java
+++
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/KaravanSchema.java
@@ -31,6 +31,7 @@ import
org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;
CamelStatusValue.Name.class,
DeploymentStatus.class,
ContainerStatus.class,
+ ContainerPort.class,
ContainerStatus.ContainerType.class,
ContainerStatus.Command.class,
ServiceStatus.class
diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts
b/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts
index 70423595..fdafeb9a 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts
@@ -79,6 +79,12 @@ export class ServiceStatus {
type: string = '';
}
+export class ContainerPort {
+ privatePort?: number;
+ publicPort?: number;
+ type: string = '';
+}
+
export class ContainerStatus {
containerName: string = '';
containerId: string = '';
@@ -93,7 +99,7 @@ export class ContainerStatus {
created: string = '';
finished: string = '';
image: string = '';
- ports: [] = [];
+ ports: ContainerPort [] = [];
commands: string [] = [];
inTransit: boolean = false;
camelRuntime: string = ''
diff --git
a/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx
b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx
index d08ee906..5a751f48 100644
---
a/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx
+++
b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx
@@ -35,7 +35,7 @@ interface Props {
container: ContainerStatus
}
-export function ContainerTableRow (props: Props) {
+export function ContainerTableRow(props: Props) {
const [isExpanded, setIsExpanded] = useState<boolean>(false);
const [showConfirmation, setShowConfirmation] = useState<boolean>(false);
@@ -59,7 +59,8 @@ export function ContainerTableRow (props: Props) {
actions={[
<Button key="confirm" variant="primary" onClick={e => {
if (command) {
- KaravanApi.manageContainer(container.env,
container.type, container.containerName, command, res => {});
+ KaravanApi.manageContainer(container.env,
container.type, container.containerName, command, res => {
+ });
setCommand(undefined);
setShowConfirmation(false);
}
@@ -114,7 +115,8 @@ export function ContainerTableRow (props: Props) {
spaceItems={{default: 'spaceItemsNone'}}>
<FlexItem>
<Tooltip content={"Start container"}
position={"bottom"}>
- <Button variant={"plain"}
icon={<PlayIcon/>} isDisabled={!commands.includes('run') || inTransit}
+ <Button variant={"plain"}
icon={<PlayIcon/>}
+
isDisabled={!commands.includes('run') || inTransit}
onClick={e => {
setCommand('run');
setShowConfirmation(true);
@@ -123,7 +125,8 @@ export function ContainerTableRow (props: Props) {
</FlexItem>
<FlexItem>
<Tooltip content={"Pause container"}
position={"bottom"}>
- <Button variant={"plain"}
icon={<PauseIcon/>} isDisabled={!commands.includes('pause') || inTransit}
+ <Button variant={"plain"}
icon={<PauseIcon/>}
+
isDisabled={!commands.includes('pause') || inTransit}
onClick={e => {
setCommand('pause');
setShowConfirmation(true);
@@ -132,7 +135,8 @@ export function ContainerTableRow (props: Props) {
</FlexItem>
<FlexItem>
<Tooltip content={"Stop container"}
position={"bottom"}>
- <Button variant={"plain"}
icon={<StopIcon/>} isDisabled={!commands.includes('stop') || inTransit}
+ <Button variant={"plain"}
icon={<StopIcon/>}
+
isDisabled={!commands.includes('stop') || inTransit}
onClick={e => {
setCommand('stop');
setShowConfirmation(true);
@@ -141,7 +145,8 @@ export function ContainerTableRow (props: Props) {
</FlexItem>
<FlexItem>
<Tooltip content={"Delete container"}
position={"bottom"}>
- <Button variant={"plain"}
icon={<DeleteIcon/>} isDisabled={!commands.includes('delete') || inTransit}
+ <Button variant={"plain"}
icon={<DeleteIcon/>}
+
isDisabled={!commands.includes('delete') || inTransit}
onClick={e => {
setCommand('delete');
setShowConfirmation(true);
@@ -157,7 +162,7 @@ export function ContainerTableRow (props: Props) {
<Td colSpan={2}>
<ExpandableRowContent>
<Flex direction={{default: "column"}}
cellPadding={"0px"}>
- {container.containerId?.substring(0, 10)+"..."}
+ {container.containerId?.substring(0, 10) + "..."}
</Flex>
</ExpandableRowContent>
</Td>
@@ -168,7 +173,16 @@ export function ContainerTableRow (props: Props) {
<Td colSpan={2}>
<ExpandableRowContent>
<Flex direction={{default: "row"}} cellPadding={"0px"}>
- {ports.map((port, index) => <FlexItem
key={index}>{port}</FlexItem>)}
+ {ports.sort((a, b) => a.privatePort &&
b.privatePort && (a.privatePort > b.privatePort) ? 1 : -1)
+ .map((port, index) => {
+ const start = port.publicPort ?
port.publicPort + "->" : "";
+ const end = port.privatePort + "/" + port.type;
+ return (
+ <FlexItem key={index}>
+ {start + end}
+ </FlexItem>
+ )
+ })}
</Flex>
</ExpandableRowContent>
</Td>
diff --git
a/karavan-web/karavan-app/src/main/webui/src/knowledgebase/components/ComponentModal.tsx
b/karavan-web/karavan-app/src/main/webui/src/knowledgebase/components/ComponentModal.tsx
index da8f1584..acffc979 100644
---
a/karavan-web/karavan-app/src/main/webui/src/knowledgebase/components/ComponentModal.tsx
+++
b/karavan-web/karavan-app/src/main/webui/src/knowledgebase/components/ComponentModal.tsx
@@ -14,20 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React from 'react';
+import React, {useState} from 'react';
import {
Button,
Modal,
ActionGroup,
Text,
CardHeader,
- Badge, Flex, CardTitle,
+ Badge, Flex, CardTitle, Tabs, Tab, TabTitleText,
} from '@patternfly/react-core';
import '../../designer/karavan.css';
import {Table, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table";
import {CamelUi} from "../../designer/utils/CamelUi";
import {ComponentApi} from "karavan-core/lib/api/ComponentApi";
-import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
+import {ComponentHeader, ComponentProperty} from
"karavan-core/lib/model/ComponentModels";
import {useKnowledgebaseStore} from "../KnowledgebaseStore";
import {shallow} from "zustand/shallow";
@@ -36,15 +36,98 @@ export function ComponentModal() {
const [component, isModalOpen, setModalOpen] = useKnowledgebaseStore((s) =>
[s.component, s.isModalOpen, s.setModalOpen], shallow)
+ const [tab, setTab] = useState<string | number>('properties');
+
const props = new Map<string, ComponentProperty>();
if (component) {
ComponentApi.getComponentProperties(component?.component.name,
"consumer").forEach(cp => props.set(cp.name, cp));
ComponentApi.getComponentProperties(component?.component.name,
"producer").forEach(cp => props.set(cp.name, cp));
}
+
+ const headers = new Map<string, ComponentHeader>();
+ if (component) {
+ ComponentApi.getComponentHeaders(component?.component.name,
"consumer").forEach(cp => headers.set(cp.name, cp));
+ ComponentApi.getComponentHeaders(component?.component.name,
"producer").forEach(cp => headers.set(cp.name, cp));
+ }
+
+
+ function getPropertiesTable() {
+ return (
+ <Table aria-label="Properties table" variant='compact'>
+ <Thead>
+ <Tr>
+ <Th key='name'>Display Name / Name</Th>
+ <Th key='desc'>Description</Th>
+ <Th key='type'>Type</Th>
+ <Th key='label'>Label</Th>
+ </Tr>
+ </Thead>
+ <Tbody>
+ {Array.from(props.values()).map((p: ComponentProperty,
idx: number) => (
+ <Tr key={idx}>
+ <Td key={`${idx}_name`}>
+ <div>
+ <b>{p.displayName}</b>
+ <div>{p.name}</div>
+ </div>
+ </Td>
+ <Td key={`${idx}_desc`}>
+ <div>
+ <div>{p.description}</div>
+ {p.defaultValue &&
p.defaultValue.toString().length > 0 &&
+ <div>{"Default value: " +
p.defaultValue}</div>}
+ </div>
+ </Td>
+ <Td key={`${idx}_type`}>{p.type}</Td>
+ <Td key={`${idx}_label`}>{p.label}</Td>
+ </Tr>
+ ))}
+ </Tbody>
+ </Table>
+ )
+ }
+
+ function getHeadersTable() {
+ return (
+ <Table aria-label="Headers table" variant='compact'>
+ <Thead>
+ <Tr>
+ <Th key='name'>Name</Th>
+ <Th key='desc' modifier={"breakWord"}>Description</Th>
+ <Th key='type'>Group</Th>
+ <Th key='label'>Java Type</Th>
+ <Th key='label'>Default Value</Th>
+ <Th key='label'>Autowired</Th>
+ </Tr>
+ </Thead>
+ <Tbody>
+ {Array.from(headers.values()).map((p: ComponentHeader,
idx: number) => (
+ <Tr key={idx}>
+ <Td key={`${idx}_name`}>
+ <div>
+ <b>{p.displayName}</b>
+ <div>{p.name}</div>
+ </div>
+ </Td>
+ <Td key={`${idx}_type`}>{p.description}</Td>
+ <Td key={`${idx}_label`}>{p.group}</Td>
+ <Td key={`${idx}_label`}>{p.javaType}</Td>
+ <Td key={`${idx}_label`}>{p.defaultValue}</Td>
+ <Td key={`${idx}_label`}>{p.autowired}</Td>
+ </Tr>
+ ))}
+ </Tbody>
+ </Table>
+ )
+ }
+
+ const showProps = props.size !== 0;
+ const showHeaders = headers.size !== 0;
+
return (
<Modal
aria-label={"Kamelet"}
- width={'fit-content'}
+ width={'80%'}
maxLength={200}
title={component?.component.title}
isOpen={isModalOpen}
@@ -70,42 +153,17 @@ export function ComponentModal() {
</CardHeader>
<Text
className="description">{component?.component.description}</Text>
- {props.size !== 0 &&
- <div>
- <CardTitle>Properties</CardTitle>
- <Table aria-label="Simple table" variant='compact'>
- <Thead>
- <Tr>
- <Th key='name'>Display Name / Name</Th>
- <Th key='desc'>Description</Th>
- <Th key='type'>Type</Th>
- <Th key='label'>Label</Th>
- </Tr>
- </Thead>
- <Tbody>
- {Array.from(props.values()).map((p:
ComponentProperty, idx: number) => (
- <Tr key={idx}>
- <Td key={`${idx}_name`}>
- <div>
- <b>{p.displayName}</b>
- <div>{p.name}</div>
- </div>
- </Td>
- <Td key={`${idx}_desc`}>
- <div>
- <div>{p.description}</div>
- {p.defaultValue &&
p.defaultValue.toString().length > 0 &&
- <div>{"Default value: " +
p.defaultValue}</div>}
- </div>
- </Td>
- <Td key={`${idx}_type`}>{p.type}</Td>
- <Td key={`${idx}_label`}>{p.label}</Td>
- </Tr>
- ))}
- </Tbody>
- </Table>
- </div>
- }
+ <Tabs
+ activeKey={tab}
+ onSelect={(event, eventKey) => setTab(eventKey)}
+ aria-label="Tabs in the default example"
+ role="region"
+ >
+ <Tab eventKey={'properties'}
title={<TabTitleText>Properties</TabTitleText>}/>
+ <Tab eventKey={'headers'}
title={<TabTitleText>Headers</TabTitleText>}/>
+ </Tabs>
+ {tab === 'properties' && showProps && getPropertiesTable()}
+ {tab === 'headers' && showHeaders && getHeadersTable()}
</Flex>
</Modal>
)
diff --git
a/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx
b/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx
index bfc76b49..cb04b01f 100644
--- a/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx
@@ -97,6 +97,7 @@ export function ServicesTableRow (props: Props) {
const env = service.environment;
const keys = Object.keys(env);
const container = props.container;
+ const ports = container?.ports || [];
const isRunning = container?.state === 'running';
const inTransit = container?.inTransit;
const color = isRunning ? "green" : "grey";
@@ -144,13 +145,29 @@ export function ServicesTableRow (props: Props) {
{keys.length > 0 && <Tr isExpanded={isExpanded}>
<Td></Td>
<Td colSpan={2}>Environment Variables</Td>
- <Td colSpan={2}>
+ <Td colSpan={1}>
<ExpandableRowContent>
<Flex direction={{default: "column"}}
cellPadding={"0px"}>
{keys.map(key => <FlexItem key={key}>{key + ": " +
env[key]}</FlexItem>)}
</Flex>
</ExpandableRowContent>
</Td>
+ <Td colSpan={1}>
+ <ExpandableRowContent>
+ <Flex direction={{default: "row"}} cellPadding={"0px"}>
+ {ports.sort((a, b) => a.privatePort &&
b.privatePort && (a.privatePort > b.privatePort) ? 1 : -1)
+ .map((port, index) => {
+ const start = port.publicPort ?
port.publicPort + "->" : "";
+ const end = port.privatePort + "/" +
port.type;
+ return (
+ <FlexItem key={index}>
+ {start + end}
+ </FlexItem>
+ )
+ })}
+ </Flex>
+ </ExpandableRowContent>
+ </Td>
</Tr>}
{healthcheck && <Tr isExpanded={isExpanded}>
<Td></Td>