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 def90df7ca63cc9e68c90a29b57f866ac46d0013
Author: Marat Gubaidullin <marat.gubaidul...@gmail.com>
AuthorDate: Thu Jul 13 20:38:59 2023 -0400

    Infrasructure config for logs #817
---
 .../apache/camel/karavan/api/DevModeResource.java  |   4 +-
 .../camel/karavan/api/InfrastructureResource.java  |   5 +-
 .../apache/camel/karavan/api/LogWatchResource.java |  44 ++--
 .../karavan/listener/DevModeCommandListener.java   |   6 +-
 .../camel/karavan/service/KaravanService.java      |   2 +-
 .../webui/src/designer/beans/BeanProperties.tsx    |  38 ++--
 .../route/property/ComponentParameterField.tsx     |  36 +--
 .../designer/route/property/DslPropertyField.tsx   |  36 +--
 .../route/property/InfrastructureSelector.tsx      |   7 +-
 .../route/property/KameletPropertyField.tsx        |  36 +--
 .../designer/route/property/KubernetesSelector.tsx | 243 ---------------------
 .../camel/karavan/bashi/ConductorService.java      |  75 ++++---
 .../org/apache/camel/karavan/bashi/Constants.java  |   1 -
 .../karavan/bashi/docker/DockerEventListener.java  |  70 ++++--
 .../camel/karavan/bashi/docker/DockerService.java  |  31 ++-
 .../camel/karavan/bashi/docker/LogCallback.java    |  26 +++
 .../camel/karavan/datagrid/DatagridService.java    |  46 ++--
 .../camel/karavan/datagrid/model/CommandName.java  |  10 -
 .../karavan/datagrid/model/ContainerInfo.java      |  70 ++++++
 .../karavan/datagrid/model/DevModeCommand.java     |  45 +++-
 .../karavan/datagrid/model/DevModeCommandName.java |  12 +
 .../karavan/datagrid/model/DevModeCommandType.java |   9 +
 .../karavan/datagrid/model/KaravanSchema.java      |   6 +-
 .../camel/karavan/datagrid/DataGridTest.java       |  22 +-
 .../src/designer/beans/BeanProperties.tsx          |  38 ++--
 .../route/property/ComponentParameterField.tsx     |  36 +--
 .../designer/route/property/DslPropertyField.tsx   |  36 +--
 .../route/property/InfrastructureSelector.tsx      |   7 +-
 .../route/property/KameletPropertyField.tsx        |  36 +--
 29 files changed, 520 insertions(+), 513 deletions(-)

diff --git 
a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java
 
b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java
index f56ad4d3..d0681bf4 100644
--- 
a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java
+++ 
b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java
@@ -57,7 +57,7 @@ public class DevModeResource {
         PodStatus status = datagridService.getDevModePodStatuses(runnerName, 
environment);
         if (status == null) {
             datagridService.saveDevModeStatus(new 
DevModeStatus(project.getProjectId(), null, null, false));
-            datagridService.sendDevModeCommand(project.getProjectId(), new 
DevModeCommand(CommandName.RUN, Instant.now().toEpochMilli()));
+            
datagridService.sendDevModeCommand(DevModeCommand.createDevModeCommand(DevModeCommandName.RUN,
 project.getProjectId()));
             return Response.ok(runnerName).build();
         }
         return Response.notModified().build();
@@ -83,7 +83,7 @@ public class DevModeResource {
     @Consumes(MediaType.APPLICATION_JSON)
     @Path("/{projectId}/{deletePVC}")
     public Response deleteRunner(@PathParam("projectId") String projectId, 
@PathParam("deletePVC") boolean deletePVC) {
-        datagridService.sendDevModeCommand(projectId, new 
DevModeCommand(CommandName.DELETE, Instant.now().toEpochMilli()));
+        
datagridService.sendDevModeCommand(DevModeCommand.createDevModeCommand(DevModeCommandName.DELETE,
 projectId));
         datagridService.deleteDevModeStatus(projectId);
         return Response.accepted().build();
     }
diff --git 
a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java
 
b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java
index d2749563..f525f6a5 100644
--- 
a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java
+++ 
b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java
@@ -203,7 +203,10 @@ public class InfrastructureResource {
         if (kubernetesService.inKubernetes()) {
             return 
Response.ok(kubernetesService.getServices(kubernetesService.getNamespace())).build();
         } else {
-            return Response.ok(List.of()).build();
+            List<String> list = 
datagridService.getContainerInfos(environment).stream()
+                    .map(ci -> ci.getPorts().stream().map(i -> 
ci.getContainerName() + ":" + i).collect(Collectors.toList()))
+                    .flatMap(List::stream).collect(Collectors.toList());
+            return Response.ok(list).build();
         }
     }
 
diff --git 
a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java
 
b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java
index 5b1ba723..0fece15e 100644
--- 
a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java
+++ 
b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java
@@ -17,6 +17,9 @@
 package org.apache.camel.karavan.api;
 
 import io.fabric8.kubernetes.client.dsl.LogWatch;
+import org.apache.camel.karavan.datagrid.DatagridService;
+import org.apache.camel.karavan.datagrid.model.DevModeCommand;
+import org.apache.camel.karavan.datagrid.model.DevModeCommandName;
 import org.apache.camel.karavan.service.KubernetesService;
 import org.eclipse.microprofile.context.ManagedExecutor;
 import org.jboss.logging.Logger;
@@ -44,6 +47,9 @@ public class LogWatchResource {
     @Inject
     KubernetesService kubernetesService;
 
+    @Inject
+    DatagridService datagridService;
+
     @Inject
     ManagedExecutor managedExecutor;
 
@@ -57,22 +63,30 @@ public class LogWatchResource {
     ) {
         managedExecutor.execute(() -> {
             LOGGER.info("LogWatch for " + name + " starting...");
-            try (SseEventSink sink = eventSink) {
-                LogWatch logWatch = type.equals("container")
-                        ? kubernetesService.getContainerLogWatch(name)
-                        : kubernetesService.getPipelineRunLogWatch(name);
-                BufferedReader reader = new BufferedReader(new 
InputStreamReader(logWatch.getOutput()));
-                try {
-                    for (String line; (line = reader.readLine()) != null && 
!sink.isClosed(); ) {
-                        sink.send(sse.newEvent(line));
-                    }
-                } catch (IOException e) {
-                    LOGGER.error(e.getMessage());
-                }
-                logWatch.close();
-                sink.close();
-                LOGGER.info("LogWatch for " + name + " closed");
+            if (kubernetesService.inKubernetes()) {
+                getKubernetesLogs(type, name, eventSink, sse);
+            } else {
+                
datagridService.sendDevModeCommand(DevModeCommand.createDevModeCommand(DevModeCommandName.LOG,
 name));
             }
         });
     }
+
+    private void getKubernetesLogs(String type, String name, SseEventSink 
eventSink, Sse sse) {
+        try (SseEventSink sink = eventSink) {
+            LogWatch logWatch = type.equals("container")
+                    ? kubernetesService.getContainerLogWatch(name)
+                    : kubernetesService.getPipelineRunLogWatch(name);
+            BufferedReader reader = new BufferedReader(new 
InputStreamReader(logWatch.getOutput()));
+            try {
+                for (String line; (line = reader.readLine()) != null && 
!sink.isClosed(); ) {
+                    sink.send(sse.newEvent(line));
+                }
+            } catch (IOException e) {
+                LOGGER.error(e.getMessage());
+            }
+            logWatch.close();
+            sink.close();
+            LOGGER.info("LogWatch for " + name + " closed");
+        }
+    }
 }
\ No newline at end of file
diff --git 
a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/listener/DevModeCommandListener.java
 
b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/listener/DevModeCommandListener.java
index 26fea711..8c1956ad 100644
--- 
a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/listener/DevModeCommandListener.java
+++ 
b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/listener/DevModeCommandListener.java
@@ -3,8 +3,8 @@ package org.apache.camel.karavan.listener;
 import io.quarkus.vertx.ConsumeEvent;
 import io.vertx.core.json.JsonObject;
 import org.apache.camel.karavan.datagrid.DatagridService;
-import org.apache.camel.karavan.datagrid.model.CommandName;
 import org.apache.camel.karavan.datagrid.model.DevModeCommand;
+import org.apache.camel.karavan.datagrid.model.DevModeCommandName;
 import org.apache.camel.karavan.datagrid.model.Project;
 import org.apache.camel.karavan.service.KubernetesService;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
@@ -34,10 +34,10 @@ public class DevModeCommandListener {
         if (kubernetesService.inKubernetes()) {
             DevModeCommand command = message.mapTo(DevModeCommand.class);
             String runnerName = command.getProjectId() + "-" + DEVMODE_SUFFIX;
-            if (Objects.equals(command.getCommandName(), CommandName.RUN)) {
+            if (Objects.equals(command.getCommandName(), 
DevModeCommandName.RUN)) {
                 Project p = datagridService.getProject(command.getProjectId());
                 kubernetesService.tryCreateRunner(p, runnerName, "");
-            } else if (Objects.equals(command.getCommandName(), 
CommandName.DELETE)){
+            } else if (Objects.equals(command.getCommandName(), 
DevModeCommandName.DELETE)){
                 kubernetesService.deleteRunner(runnerName, false);
                 datagridService.deleteDevModeStatus(command.getProjectId());
             }
diff --git 
a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java
 
b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java
index 177401a6..b0b1f359 100644
--- 
a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java
+++ 
b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java
@@ -48,7 +48,7 @@ public class KaravanService {
     void onStart(@Observes StartupEvent ev) {
         LOGGER.info("Start Karavan");
         datagridService.start();
-//        datagridService.clearAllStatuses();
+        datagridService.clearAllStatuses();
         setEnvironment();
         initialImport();
         startInformers();
diff --git 
a/karavan-cloud/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx
 
b/karavan-cloud/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx
index b7ab5532..24c1e202 100644
--- 
a/karavan-cloud/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx
+++ 
b/karavan-cloud/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx
@@ -54,9 +54,9 @@ interface State {
     bean?: NamedBeanDefinition
     properties: Map<string, [string, string, boolean]>
     key: string,
-    showKubernetesSelector: boolean
-    kubernetesSelectorUuid?: string
-    kubernetesSelectorProperty?: string
+    showInfrastructureSelector: boolean
+    infrastructureSelectorUuid?: string
+    infrastructureSelectorProperty?: string
 }
 
 export class BeanProperties extends React.Component<Props, State> {
@@ -70,7 +70,7 @@ export class BeanProperties extends React.Component<Props, 
State> {
     public state: State = {
         bean: this.props.bean,
         key: '',
-        showKubernetesSelector: false,
+        showInfrastructureSelector: false,
         properties: this.props.bean?.properties ? 
this.preparePropertiesMap(this.props.bean?.properties) : new Map<string, 
[string, string, boolean]>()
     };
 
@@ -118,31 +118,31 @@ export class BeanProperties extends 
React.Component<Props, State> {
         })
     }
 
-    selectKubernetes = (value: string) => {
-        const propertyId = this.state.kubernetesSelectorProperty;
-        const uuid = this.state.kubernetesSelectorUuid;
+    selectInfrastructure = (value: string) => {
+        const propertyId = this.state.infrastructureSelectorProperty;
+        const uuid = this.state.infrastructureSelectorUuid;
         if (propertyId && uuid){
             if (value.startsWith("config") || value.startsWith("secret")) 
value = "{{" + value + "}}";
             this.propertyChanged(uuid, propertyId, value, false);
-            this.setState({showKubernetesSelector: false, 
kubernetesSelectorProperty: undefined})
+            this.setState({showInfrastructureSelector: false, 
infrastructureSelectorProperty: undefined})
         }
     }
 
-    openKubernetesSelector = (uuid: string, propertyName: string) => {
-        this.setState({kubernetesSelectorUuid: uuid, 
kubernetesSelectorProperty: propertyName, showKubernetesSelector: true});
+    openInfrastructureSelector = (uuid: string, propertyName: string) => {
+        this.setState({infrastructureSelectorUuid: uuid, 
infrastructureSelectorProperty: propertyName, showInfrastructureSelector: 
true});
     }
 
-    closeKubernetesSelector = () => {
-        this.setState({showKubernetesSelector: false})
+    closeInfrastructureSelector = () => {
+        this.setState({showInfrastructureSelector: false})
     }
 
-    getKubernetesSelectorModal() {
+    getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.showKubernetesSelector}
-                onClose={() => this.closeKubernetesSelector()}
-                onSelect={this.selectKubernetes}/>)
+                isOpen={this.state.showInfrastructureSelector}
+                onClose={() => this.closeInfrastructureSelector()}
+                onSelect={this.selectInfrastructure}/>)
     }
 
     cloneBean = () => {
@@ -208,8 +208,8 @@ export class BeanProperties extends React.Component<Props, 
State> {
                                            onChange={e => 
this.propertyChanged(i, e, value, showPassword)}/>
                                 <InputGroup>
                                     {inInfrastructure &&
-                                        <Tooltip position="bottom-end" 
content="Select value from Kubernetes">
-                                        <Button variant="control" onClick={e 
=> this.openKubernetesSelector(i, key)}>
+                                        <Tooltip position="bottom-end" 
content="Select value from Infrastructure">
+                                        <Button variant="control" onClick={e 
=> this.openInfrastructureSelector(i, key)}>
                                             {icon}
                                         </Button>
                                     </Tooltip>}
@@ -246,7 +246,7 @@ export class BeanProperties extends React.Component<Props, 
State> {
                     {this.state.bean === undefined && <IntegrationHeader 
integration={this.props.integration}/>}
                     {this.state.bean !== undefined && this.getBeanForm()}
                 </Form>
-                {this.getKubernetesSelectorModal()}
+                {this.getInfrastructureSelectorModal()}
             </div>
         )
     }
diff --git 
a/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx
 
b/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx
index 39817f7c..ca1f6a0d 100644
--- 
a/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx
+++ 
b/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx
@@ -23,7 +23,7 @@ import {
     Select,
     SelectVariant,
     SelectDirection,
-    SelectOption, InputGroup, TextArea, Tooltip, Button,
+    SelectOption, InputGroup, TextArea, Tooltip, Button, capitalize,
 } from '@patternfly/react-core';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -57,8 +57,8 @@ interface State {
     selectStatus: Map<string, boolean>
     showEditor: boolean
     showPassword: boolean
-    showKubernetesSelector: boolean
-    kubernetesSelectorProperty?: string
+    showInfrastructureSelector: boolean
+    infrastructureSelectorProperty?: string
     ref: any
     id: string
 }
@@ -69,7 +69,7 @@ export class ComponentParameterField extends 
React.Component<Props, State> {
         selectStatus: new Map<string, boolean>(),
         showEditor: false,
         showPassword: false,
-        showKubernetesSelector: false,
+        showInfrastructureSelector: false,
         ref: React.createRef(),
         id: prefix + "-" + this.props.property.name
     }
@@ -179,7 +179,7 @@ export class ComponentParameterField extends 
React.Component<Props, State> {
         </InputGroup>
     }
 
-    selectKubernetes = (value: string) => {
+    selectInfrastructure = (value: string) => {
         // check if there is a selection
         const textVal = this.state.ref.current;
         const cursorStart = textVal.selectionStart;
@@ -189,29 +189,29 @@ export class ComponentParameterField extends 
React.Component<Props, State> {
             const selectedText = prevValue.substring(cursorStart, cursorEnd)
             value = prevValue.replace(selectedText, value);
         }
-        const propertyName = this.state.kubernetesSelectorProperty;
+        const propertyName = this.state.infrastructureSelectorProperty;
         if (propertyName) {
             if (value.startsWith("config") || value.startsWith("secret")) 
value = "{{" + value + "}}";
             this.parametersChanged(propertyName, value);
-            this.setState({showKubernetesSelector: false, 
kubernetesSelectorProperty: undefined})
+            this.setState({showInfrastructureSelector: false, 
infrastructureSelectorProperty: undefined})
         }
     }
 
-    openKubernetesSelector = (propertyName: string) => {
-        this.setState({kubernetesSelectorProperty: propertyName, 
showKubernetesSelector: true});
+    openInfrastructureSelector = (propertyName: string) => {
+        this.setState({infrastructureSelectorProperty: propertyName, 
showInfrastructureSelector: true});
     }
 
-    closeKubernetesSelector = () => {
-        this.setState({showKubernetesSelector: false})
+    closeInfrastructureSelector = () => {
+        this.setState({showInfrastructureSelector: false})
     }
 
-    getKubernetesSelectorModal() {
+    getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.showKubernetesSelector}
-                onClose={() => this.closeKubernetesSelector()}
-                onSelect={this.selectKubernetes}/>)
+                isOpen={this.state.showInfrastructureSelector}
+                onClose={() => this.closeInfrastructureSelector()}
+                onSelect={this.selectInfrastructure}/>)
     }
 
     getStringInput(property: ComponentProperty, value: any) {
@@ -221,8 +221,8 @@ export class ComponentParameterField extends 
React.Component<Props, State> {
         const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? 
<KubernetesIcon/> : <DockerIcon/>
         return <InputGroup>
             {inInfrastructure && !showEditor && !noInfraSelectorButton &&
-                <Tooltip position="bottom-end" content="Select from 
Kubernetes">
-                    <Button variant="control" onClick={e => 
this.openKubernetesSelector(property.name)}>
+                <Tooltip position="bottom-end" content={"Select from " + 
capitalize((InfrastructureAPI.infrastructure))}>
+                    <Button variant="control" onClick={e => 
this.openInfrastructureSelector(property.name)}>
                         {icon}
                     </Button>
                 </Tooltip>}
@@ -340,7 +340,7 @@ export class ComponentParameterField extends 
React.Component<Props, State> {
                     && this.getSelect(property, value)}
                 {property.type === 'boolean'
                     && this.getSwitch(property, value)}
-                {this.getKubernetesSelectorModal()}
+                {this.getInfrastructureSelectorModal()}
             </FormGroup>
         )
     }
diff --git 
a/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
 
b/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
index e11ee485..d17a26c3 100644
--- 
a/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
+++ 
b/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
@@ -35,7 +35,7 @@ import {
     Text,
     Tooltip,
     Card,
-    InputGroup,
+    InputGroup, capitalize,
 } from '@patternfly/react-core';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -87,8 +87,8 @@ interface State {
     isShowAdvanced: Map<string, boolean>,
     arrayValues: Map<string, string>,
     showEditor: boolean
-    showKubernetesSelector: boolean
-    kubernetesSelectorProperty?: string
+    infrastructureSelector: boolean
+    infrastructureSelectorProperty?: string
     customCode?: string
     ref: any
 }
@@ -100,7 +100,7 @@ export class DslPropertyField extends 
React.Component<Props, State> {
         arrayValues: new Map<string, string>(),
         isShowAdvanced: new Map<string, boolean>(),
         showEditor: false,
-        showKubernetesSelector: false,
+        infrastructureSelector: false,
         ref: React.createRef(),
     };
 
@@ -176,7 +176,7 @@ export class DslPropertyField extends 
React.Component<Props, State> {
         return property.name === 'uri' && !['ToDefinition', 
'ToDynamicDefinition', 'WireTapDefinition'].includes(dslName)
     }
 
-    selectKubernetes = (value: string) => {
+    selectInfrastructure = (value: string) => {
         // check if there is a selection
         const textVal = this.state.ref.current;
         const cursorStart = textVal.selectionStart;
@@ -186,29 +186,29 @@ export class DslPropertyField extends 
React.Component<Props, State> {
             const selectedText = prevValue.substring(cursorStart, cursorEnd)
             value = prevValue.replace(selectedText, value);
         }
-        const propertyName = this.state.kubernetesSelectorProperty;
+        const propertyName = this.state.infrastructureSelectorProperty;
         if (propertyName) {
             if (value.startsWith("config") || value.startsWith("secret")) 
value = "{{" + value + "}}";
             this.propertyChanged(propertyName, value);
-            this.setState({showKubernetesSelector: false, 
kubernetesSelectorProperty: undefined})
+            this.setState({infrastructureSelector: false, 
infrastructureSelectorProperty: undefined})
         }
     }
 
-    openKubernetesSelector = (propertyName: string) => {
-        this.setState({kubernetesSelectorProperty: propertyName, 
showKubernetesSelector: true});
+    openInfrastructureSelector = (propertyName: string) => {
+        this.setState({infrastructureSelectorProperty: propertyName, 
infrastructureSelector: true});
     }
 
-    closeKubernetesSelector = () => {
-        this.setState({showKubernetesSelector: false})
+    closeInfrastructureSelector = () => {
+        this.setState({infrastructureSelector: false})
     }
 
-    getKubernetesSelectorModal() {
+    getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.showKubernetesSelector}
-                onClose={() => this.closeKubernetesSelector()}
-                onSelect={this.selectKubernetes}/>)
+                isOpen={this.state.infrastructureSelector}
+                onClose={() => this.closeInfrastructureSelector()}
+                onSelect={this.selectInfrastructure}/>)
     }
 
     getStringInput = (property: PropertyMeta, value: any) => {
@@ -218,8 +218,8 @@ export class DslPropertyField extends 
React.Component<Props, State> {
         const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? 
<KubernetesIcon/> : <DockerIcon/>
         return (<InputGroup>
             {inInfrastructure && !showEditor && !noInfraSelectorButton &&
-                <Tooltip position="bottom-end" content="Select from 
Kubernetes">
-                    <Button variant="control" onClick={e => 
this.openKubernetesSelector(property.name)}>
+                <Tooltip position="bottom-end" content={"Select from " + 
capitalize(InfrastructureAPI.infrastructure)}>
+                    <Button variant="control" onClick={e => 
this.openInfrastructureSelector(property.name)}>
                         {icon}
                     </Button>
                 </Tooltip>}
@@ -732,7 +732,7 @@ export class DslPropertyField extends 
React.Component<Props, State> {
                     {isKamelet && property.name === 'parameters' && 
this.getKameletParameters()}
                     {!isKamelet && property.name === 'parameters' && 
this.getComponentParameters(property)}
                 </FormGroup>
-                {this.getKubernetesSelectorModal()}
+                {this.getInfrastructureSelectorModal()}
             </div>
         )
     }
diff --git 
a/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/InfrastructureSelector.tsx
 
b/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/InfrastructureSelector.tsx
index 04c8d43a..e0069bc0 100644
--- 
a/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/InfrastructureSelector.tsx
+++ 
b/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/InfrastructureSelector.tsx
@@ -208,6 +208,7 @@ export class InfrastructureSelector extends 
React.Component<Props, State> {
 
     render() {
         const tabIndex = this.state.tabIndex;
+        const tabs = InfrastructureAPI.infrastructure === 'kubernetes' ? 
['configMap', 'secret', 'services'] : ['services'];
         return (
             <Modal
                 aria-label="Select from Infrastructure"
@@ -223,9 +224,7 @@ export class InfrastructureSelector extends 
React.Component<Props, State> {
                         </FlexItem>
                         <FlexItem>
                             <Tabs style={{overflow: 'hidden'}} 
activeKey={this.state.tabIndex} onSelect={this.selectTab}>
-                                <Tab eventKey={"configMap"} key={"configMap"} 
title={<TabTitleText>ConfigMaps</TabTitleText>} />
-                                <Tab eventKey={"secret"} key={"secret"} 
title={<TabTitleText>Secrets</TabTitleText>} />
-                                <Tab eventKey={"service"} key={"service"} 
title={<TabTitleText>Services</TabTitleText>} />
+                                {tabs.map(tab => <Tab eventKey={tab} key={tab} 
title={<TabTitleText>{capitalize(tab)}</TabTitleText>} />)}
                             </Tabs>
                         </FlexItem>
                     </Flex>
@@ -235,7 +234,7 @@ export class InfrastructureSelector extends 
React.Component<Props, State> {
                     {this.searchInput()}
                     {tabIndex === 'configMap' && this.getConfigMapTable()}
                     {tabIndex === 'secret' && this.getSecretsTable()}
-                    {tabIndex === 'service' && this.getServicesTable()}
+                    {tabIndex === 'services' && this.getServicesTable()}
                 </PageSection>
             </Modal>
         )
diff --git 
a/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/KameletPropertyField.tsx
 
b/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/KameletPropertyField.tsx
index 8beed690..9b0a3b39 100644
--- 
a/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/KameletPropertyField.tsx
+++ 
b/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/KameletPropertyField.tsx
@@ -19,7 +19,7 @@ import {
     FormGroup,
     TextInput,
     Popover,
-    Switch, InputGroup, Button, TextArea, Tooltip
+    Switch, InputGroup, Button, TextArea, Tooltip, capitalize
 } from '@patternfly/react-core';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -45,8 +45,8 @@ interface State {
     selectIsOpen: boolean
     showEditor: boolean
     showPassword: boolean
-    showKubernetesSelector: boolean
-    kubernetesSelectorProperty?: string
+    showInfrastructureSelector: boolean
+    infrastructureSelectorProperty?: string
     ref: any
 }
 
@@ -56,7 +56,7 @@ export class KameletPropertyField extends 
React.Component<Props, State> {
         selectIsOpen: false,
         showEditor: false,
         showPassword: false,
-        showKubernetesSelector: false,
+        showInfrastructureSelector: false,
         ref: React.createRef(),
     }
 
@@ -69,7 +69,7 @@ export class KameletPropertyField extends 
React.Component<Props, State> {
         this.setState({selectIsOpen: false});
     }
 
-    selectKubernetes = (value: string) => {
+    selectInfrastructure = (value: string) => {
         // check if there is a selection
         const textVal = this.state.ref.current;
         const cursorStart = textVal.selectionStart;
@@ -79,29 +79,29 @@ export class KameletPropertyField extends 
React.Component<Props, State> {
             const selectedText = prevValue.substring(cursorStart, cursorEnd)
             value = prevValue.replace(selectedText, value);
         }
-        const propertyId = this.state.kubernetesSelectorProperty;
+        const propertyId = this.state.infrastructureSelectorProperty;
         if (propertyId){
             if (value.startsWith("config") || value.startsWith("secret")) 
value = "{{" + value + "}}";
             this.parametersChanged(propertyId, value);
-            this.setState({showKubernetesSelector: false, 
kubernetesSelectorProperty: undefined})
+            this.setState({showInfrastructureSelector: false, 
infrastructureSelectorProperty: undefined})
         }
     }
 
-    openKubernetesSelector = (propertyName: string) => {
-        this.setState({kubernetesSelectorProperty: propertyName, 
showKubernetesSelector: true});
+    openInfrastructureSelector = (propertyName: string) => {
+        this.setState({infrastructureSelectorProperty: propertyName, 
showInfrastructureSelector: true});
     }
 
-    closeKubernetesSelector = () => {
-        this.setState({showKubernetesSelector: false})
+    closeInfrastructureSelector = () => {
+        this.setState({showInfrastructureSelector: false})
     }
 
-    getKubernetesSelectorModal() {
+    getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.showKubernetesSelector}
-                onClose={() => this.closeKubernetesSelector()}
-                onSelect={this.selectKubernetes}/>)
+                isOpen={this.state.showInfrastructureSelector}
+                onClose={() => this.closeInfrastructureSelector()}
+                onSelect={this.selectInfrastructure}/>)
     }
 
     getStringInput() {
@@ -115,8 +115,8 @@ export class KameletPropertyField extends 
React.Component<Props, State> {
         const showInfraSelectorButton = inInfrastructure && !showEditor && 
!noInfraSelectorButton
         return <InputGroup>
             {showInfraSelectorButton  &&
-                <Tooltip position="bottom-end" content="Select from 
Kubernetes">
-                    <Button variant="control" onClick={e => 
this.openKubernetesSelector(property.id)}>
+                <Tooltip position="bottom-end" content={"Select from " + 
capitalize(InfrastructureAPI.infrastructure)}>
+                    <Button variant="control" onClick={e => 
this.openInfrastructureSelector(property.id)}>
                         {icon}
                     </Button>
                 </Tooltip>}
@@ -197,7 +197,7 @@ export class KameletPropertyField extends 
React.Component<Props, State> {
                         onChange={e => this.parametersChanged(property.id, 
!Boolean(value))}/>
                     }
                 </FormGroup>
-                {this.getKubernetesSelectorModal()}
+                {this.getInfrastructureSelectorModal()}
             </div>
         )
     }
diff --git 
a/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/KubernetesSelector.tsx
 
b/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/KubernetesSelector.tsx
deleted file mode 100644
index b7997eda..00000000
--- 
a/karavan-cloud/karavan-app/src/main/webui/src/designer/route/property/KubernetesSelector.tsx
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * 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.
- */
-import React from 'react';
-import {
-    Badge,
-    Button, Flex, FlexItem,
-    Form, FormGroup, Modal, PageSection,
-    Tab, Tabs, TabTitleText, TextInput,
-} from '@patternfly/react-core';
-import '../../karavan.css';
-import {TableComposable, Tbody, Td, Th, Thead, Tr} from 
"@patternfly/react-table";
-import {InfrastructureAPI} from "../../utils/InfrastructureAPI";
-
-interface Props {
-    onSelect: (value: string) => void,
-    onClose?: () => void,
-    isOpen: boolean,
-    dark: boolean,
-}
-
-interface State {
-    tabIndex: string | number
-    filter?: string
-    configMaps:  string[]
-    secrets:  string[]
-    services:  string[]
-}
-
-export class KubernetesSelector extends React.Component<Props, State> {
-
-    public state: State = {
-        tabIndex: "configMap",
-        configMaps: InfrastructureAPI.configMaps,
-        secrets: InfrastructureAPI.secrets,
-        services: InfrastructureAPI.services
-    };
-
-    selectTab = (evt: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: 
string | number) => {
-        this.setState({tabIndex: eventKey})
-    }
-
-    checkFilter = (name: string): boolean => {
-        if (this.state.filter !== undefined && name) {
-            return name.toLowerCase().includes(this.state.filter.toLowerCase())
-        } else {
-            return true;
-        }
-    }
-
-    searchInput = () => {
-        return (
-            <Form isHorizontal className="search" autoComplete="off">
-                <FormGroup fieldId="search">
-                    <TextInput className="text-field" type="text" id="search" 
name="search" iconVariant='search'
-                               value={this.state.filter}
-                               onChange={e => this.setState({filter: e})}/>
-                </FormGroup>
-            </Form>
-        )
-    }
-
-    getConfigMapTable() {
-        const configMaps = this.state.configMaps;
-        return (
-            <TableComposable variant='compact' borders={false}>
-                <Thead>
-                    <Tr>
-                        <Th/>
-                        <Th key='name'>Name</Th>
-                        <Th key='data'>Data</Th>
-                    </Tr>
-                </Thead>
-                <Tbody>
-                    {configMaps
-                        .filter(name => this.checkFilter(name))
-                        .map((name, idx: number) => {
-                            const configMapName = name.split("/")[0];
-                            const data = name.split("/")[1];
-                            return (
-                                <Tr key={name}>
-                                    <Td noPadding isActionCell>
-                                        <Badge>CM</Badge>
-                                    </Td>
-                                    <Td noPadding>
-                                        {configMapName}
-                                    </Td>
-                                    <Td noPadding>
-                                        <Button style={{padding: '6px'}} 
variant={"link"} onClick={
-                                            e => 
this.props.onSelect?.call(this, "configmap:" + name)}>
-                                            {data}
-                                        </Button>
-                                    </Td>
-                                </Tr>
-                            )
-                        })}
-                </Tbody>
-            </TableComposable>
-        )
-    }
-
-    getSecretsTable() {
-        const secrets = this.state.secrets;
-        return (
-            <TableComposable variant='compact' borders={false}>
-                <Thead>
-                    <Tr>
-                        <Th/>
-                        <Th key='name'>Name</Th>
-                        <Th key='data'>Data</Th>
-                    </Tr>
-                </Thead>
-                <Tbody>
-                    {secrets
-                        .filter(name => this.checkFilter(name))
-                        .map((name, idx: number) => {
-                            const configMapName = name.split("/")[0];
-                            const data = name.split("/")[1];
-                            return (
-                                <Tr key={name}>
-                                    <Td noPadding isActionCell>
-                                        <Badge>S</Badge>
-                                    </Td>
-                                    <Td noPadding>
-                                        {configMapName}
-                                    </Td>
-                                    <Td noPadding>
-                                        <Button style={{padding: '6px'}} 
variant={"link"} onClick={
-                                            e => 
this.props.onSelect?.call(this, "secret:" + name)}>
-                                            {data}
-                                        </Button>
-                                    </Td>
-                                </Tr>
-                            )
-                        })}
-                </Tbody>
-            </TableComposable>
-        )
-    }
-
-    getServicesTable() {
-        const services = this.state.services;
-        return (
-            <TableComposable variant='compact' borders={false}>
-                <Thead>
-                    <Tr>
-                        <Th/>
-                        <Th key='name'>Name</Th>
-                        {/*<Th key='hostPort'>Host:Port</Th>*/}
-                        <Th key='host'>Host</Th>
-                        <Th key='port'>Port</Th>
-                    </Tr>
-                </Thead>
-                <Tbody>
-                    {services
-                        .filter(name => this.checkFilter(name))
-                        .map((name, idx: number) => {
-                            const serviceName = name.split("|")[0];
-                            const hostPort = name.split("|")[1];
-                            const host = hostPort.split(":")[0];
-                            const port = hostPort.split(":")[1];
-                            return (
-                                <Tr key={name}>
-                                    <Td noPadding isActionCell>
-                                        <Badge>S</Badge>
-                                    </Td>
-                                    {/*<Td noPadding>*/}
-                                    {/*    {serviceName}*/}
-                                    {/*</Td>*/}
-                                    <Td noPadding>
-                                        <Button style={{padding: '6px'}} 
variant={"link"} onClick={
-                                            e => 
this.props.onSelect?.call(this, hostPort)}>
-                                            {serviceName}
-                                        </Button>
-                                    </Td>
-                                    <Td noPadding>
-                                        <Button style={{padding: '6px'}} 
variant={"link"} onClick={
-                                            e => 
this.props.onSelect?.call(this, host)}>
-                                            {host}
-                                        </Button>
-                                    </Td>
-                                    <Td noPadding>
-                                        <Button style={{padding: '6px'}} 
variant={"link"} onClick={
-                                            e => 
this.props.onSelect?.call(this, port)}>
-                                            {port}
-                                        </Button>
-                                    </Td>
-                                </Tr>
-                            )
-                        })}
-                </Tbody>
-            </TableComposable>
-        )
-    }
-
-    render() {
-        const tabIndex = this.state.tabIndex;
-        return (
-            <Modal
-                aria-label="Select from Kubernetes"
-                width={'50%'}
-                className='dsl-modal'
-                isOpen={this.props.isOpen}
-                onClose={this.props.onClose}
-                header={
-                    <Flex direction={{default: "column"}}>
-                        <FlexItem>
-                            <h3>{"Select from Kubernetes"}</h3>
-                            {this.searchInput()}
-                        </FlexItem>
-                        <FlexItem>
-                            <Tabs style={{overflow: 'hidden'}} 
activeKey={this.state.tabIndex} onSelect={this.selectTab}>
-                                <Tab eventKey={"configMap"} key={"configMap"} 
title={<TabTitleText>ConfigMaps</TabTitleText>} />
-                                <Tab eventKey={"secret"} key={"secret"} 
title={<TabTitleText>Secrets</TabTitleText>} />
-                                <Tab eventKey={"service"} key={"service"} 
title={<TabTitleText>Services</TabTitleText>} />
-                            </Tabs>
-                        </FlexItem>
-                    </Flex>
-                }
-                actions={{}}>
-                <PageSection variant={this.props.dark ? "darker" : "light"}>
-                    {this.searchInput()}
-                    {tabIndex === 'configMap' && this.getConfigMapTable()}
-                    {tabIndex === 'secret' && this.getSecretsTable()}
-                    {tabIndex === 'service' && this.getServicesTable()}
-                </PageSection>
-            </Modal>
-        )
-    }
-}
\ No newline at end of file
diff --git 
a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/ConductorService.java
 
b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/ConductorService.java
index 09548632..c9998219 100644
--- 
a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/ConductorService.java
+++ 
b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/ConductorService.java
@@ -102,32 +102,6 @@ public class ConductorService {
         }
     }
 
-    @ConsumeEvent(value = DatagridService.ADDRESS_DEVMODE_COMMAND, blocking = 
true, ordered = true)
-    void receiveCommand(JsonObject message) throws InterruptedException {
-        LOGGER.info("DevMode Command: " + message);
-        DevModeCommand command = message.mapTo(DevModeCommand.class);
-        String containerName = command.getProjectId() + "-" + DEVMODE_SUFFIX;
-        Project p = datagridService.getProject(command.getProjectId());
-        if (Objects.equals(command.getCommandName(), CommandName.RUN)) {
-            LOGGER.infof("DevMode starting for %s", p.getProjectId());
-
-            HealthCheck healthCheck = new 
HealthCheck().withTest(List.of("CMD", "curl", "-f", 
"http://localhost:8080/q/dev/health";))
-                    
.withInterval(10000000000L).withTimeout(10000000000L).withStartPeriod(10000000000L).withRetries(30);
-
-            dockerService.createContainer(containerName, runnerImage,
-                    List.of(), "", false, healthCheck,
-                    Map.of("type", "devmode", "projectId", p.getProjectId())
-            );
-            dockerService.startContainer(containerName);
-            LOGGER.infof("DevMode started for %s", p.getProjectId());
-
-        } else if (Objects.equals(command.getCommandName(), 
CommandName.DELETE)){
-            dockerService.stopContainer(containerName);
-            dockerService.deleteContainer(containerName);
-            datagridService.deleteDevModeStatus(p.getName());
-        }
-    }
-
     @ConsumeEvent(value = ADDRESS_CONTAINER_STATS, blocking = true, ordered = 
true)
     public void saveStats(JsonObject data) {
         String projectId = data.getString("projectId");
@@ -142,4 +116,53 @@ public class ConductorService {
             }
         }
     }
+
+    @ConsumeEvent(value = DatagridService.ADDRESS_DEVMODE_COMMAND, blocking = 
true, ordered = true)
+    void receiveCommand(JsonObject message) throws InterruptedException {
+        LOGGER.info("DevMode Command: " + message);
+        DevModeCommand command = message.mapTo(DevModeCommand.class);
+        switch (command.getCommandName()){
+            case RUN:
+                runContainer(command);
+                break;
+            case DELETE:
+                deleteContainer(command);
+                break;
+            case LOG:
+                logContainer(command);
+                break;
+        }
+        datagridService.deleteDevModeCommand(command);
+    }
+
+    void runContainer(DevModeCommand command) throws InterruptedException {
+        if (DevModeCommandType.DEVMODE.equals(command.getType())) {
+            String projectId = command.getProjectId();
+            LOGGER.infof("DevMode starting for %s", projectId);
+            HealthCheck healthCheck = new 
HealthCheck().withTest(List.of("CMD", "curl", "-f", 
"http://localhost:8080/q/dev/health";))
+                    
.withInterval(10000000000L).withTimeout(10000000000L).withStartPeriod(10000000000L).withRetries(30);
+            dockerService.createContainer(command.getContainerName(), 
runnerImage,
+                    List.of(), "", false, healthCheck,
+                    Map.of("type", "devmode", "projectId", projectId));
+            dockerService.startContainer(command.getContainerName());
+            LOGGER.infof("DevMode started for %s", projectId);
+        } else {
+
+        }
+    }
+
+    void deleteContainer(DevModeCommand command) {
+        if (DevModeCommandType.DEVMODE.equals(command.getType())) {
+            datagridService.deleteDevModeStatus(command.getProjectId());
+            dockerService.stopContainer(command.getContainerName());
+            dockerService.deleteContainer(command.getContainerName());
+        } else {
+            dockerService.stopContainer(command.getContainerName());
+            dockerService.deleteContainer(command.getContainerName());
+        }
+    }
+
+    void logContainer(DevModeCommand command) {
+        dockerService.logContainer(command.getContainerName());
+    }
 }
\ No newline at end of file
diff --git 
a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/Constants.java
 
b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/Constants.java
index a0575eb2..38c3a89e 100644
--- 
a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/Constants.java
+++ 
b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/Constants.java
@@ -6,5 +6,4 @@ public class Constants {
     public static final String DATAGRID_CONTAINER_NAME = "infinispan";
 
     public static final String KARAVAN_CONTAINER_NAME = "karavan";
-    public static final String DEVMODE_SUFFIX = "devmode";
 }
diff --git 
a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerEventListener.java
 
b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerEventListener.java
index 4910e603..9b57dc72 100644
--- 
a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerEventListener.java
+++ 
b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerEventListener.java
@@ -2,12 +2,13 @@ package org.apache.camel.karavan.bashi.docker;
 
 import com.github.dockerjava.api.async.ResultCallback;
 import com.github.dockerjava.api.model.Container;
+import com.github.dockerjava.api.model.ContainerPort;
 import com.github.dockerjava.api.model.Event;
 import com.github.dockerjava.api.model.EventType;
 import io.vertx.core.eventbus.EventBus;
 import org.apache.camel.karavan.bashi.ConductorService;
-import org.apache.camel.karavan.bashi.Constants;
 import org.apache.camel.karavan.datagrid.DatagridService;
+import org.apache.camel.karavan.datagrid.model.ContainerInfo;
 import org.apache.camel.karavan.datagrid.model.DevModeStatus;
 import org.apache.camel.karavan.datagrid.model.PodStatus;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
@@ -19,7 +20,11 @@ import java.io.Closeable;
 import java.io.IOException;
 import java.time.Instant;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static 
org.apache.camel.karavan.datagrid.model.DevModeCommand.DEVMODE_SUFFIX;
 
 @ApplicationScoped
 public class DockerEventListener implements ResultCallback<Event> {
@@ -48,32 +53,61 @@ public class DockerEventListener implements 
ResultCallback<Event> {
         try {
             if (Objects.equals(event.getType(), EventType.CONTAINER)) {
                 Container container = 
dockerService.getContainer(event.getId());
+                onContainerEvent(event, container);
                 String status = event.getStatus();
                 if (container.getNames()[0].equals("/infinispan") && 
status.startsWith("health_status:")) {
+                    onInfinispanHealthEvent(event, container);
+                } else if (container.getNames()[0].endsWith(DEVMODE_SUFFIX) || 
Objects.equals(container.getLabels().get("type"), "devmode")) {
+                    onDevModeEvent(event, container);
+                }
+            }
+        } catch (Exception exception) {
+            LOGGER.error(exception.getMessage());
+        }
+    }
+
+    public void onContainerEvent(Event event, Container container) {
+        if (datagridService.isReady()) {
+            String name = container.getNames()[0].replace("/", "");
+            if (Arrays.asList("destroy", "stop", "die", "kill", "pause", 
"destroy", "rename").contains(event.getStatus())) {
+                datagridService.deleteContainerInfo(name);
+            } else if (Arrays.asList("create", "start", 
"unpause").contains(event.getStatus())) {
+                List<Integer> ports = 
Arrays.stream(container.getPorts()).map(ContainerPort::getPrivatePort).filter(Objects::nonNull).collect(Collectors.toList());
+                ContainerInfo ci = new ContainerInfo(name, container.getId(), 
container.getImage(), ports, environment);
+                datagridService.saveContainerInfo(ci);
+            }
+        }
+    }
+
+    public void onInfinispanHealthEvent(Event event, Container container) {
+        String status = event.getStatus();
+        String health = status.replace("health_status: ", "");
+        LOGGER.infof("Container %s health status: %s", 
container.getNames()[0], health);
+        eventBus.publish(ConductorService.ADDRESS_INFINISPAN_HEALTH, health);
+    }
+
+    public void onDevModeEvent(Event event, Container container) {
+        try {
+            if (datagridService.isReady()) {
+                String status = event.getStatus();
+                String name = container.getNames()[0].replace("/", "");
+                if (Arrays.asList("stop", "die", "kill", "pause", 
"destroy").contains(event.getStatus())) {
+                    String projectId = name.replace(DEVMODE_SUFFIX, "");
+                    datagridService.deletePodStatus(projectId, environment, 
name);
+                    datagridService.deleteCamelStatuses(projectId, 
environment);
+                } else if (Arrays.asList("start", 
"unpause").contains(event.getStatus())) {
+                    String projectId = name.replace(DEVMODE_SUFFIX, "");
+                    PodStatus ps = new PodStatus(name, true, null, projectId, 
environment, true, Instant.ofEpochSecond(container.getCreated()).toString());
+                    datagridService.savePodStatus(ps);
+                } else if (status.startsWith("health_status:")) {
                     String health = status.replace("health_status: ", "");
                     LOGGER.infof("Container %s health status: %s", 
container.getNames()[0], health);
-                    
eventBus.publish(ConductorService.ADDRESS_INFINISPAN_HEALTH, health);
-                } else if (Objects.equals(container.getLabels().get("type"), 
"devmode") && status.startsWith("health_status:")) {
-                    String health = status.replace("health_status: ", "");
-                    LOGGER.infof("Container %s health status: %s", 
container.getNames()[0], health);
-//                     update DevModeStatus
+                    //update DevModeStatus
                     String containerName = 
container.getNames()[0].replace("/", "");
                     DevModeStatus dms = 
datagridService.getDevModeStatus(container.getLabels().get("projectId"));
                     dms.setContainerName(containerName);
                     dms.setContainerId(container.getId());
                     datagridService.saveDevModeStatus(dms);
-                } else if 
(container.getNames()[0].endsWith(Constants.DEVMODE_SUFFIX)) {
-                    if (Arrays.asList("stop", "die", "kill", "pause", 
"destroy").contains(event.getStatus())) {
-                        String name = container.getNames()[0].replace("/", "");
-                        String projectId = name.replace("-" + 
Constants.DEVMODE_SUFFIX, "");
-                        datagridService.deletePodStatus(projectId, 
environment, name);
-                        datagridService.deleteCamelStatuses(projectId, 
environment);
-                    } else if (Arrays.asList("start", 
"unpause").contains(event.getStatus())) {
-                        String name = container.getNames()[0].replace("/", "");
-                        String projectId = name.replace("-" + 
Constants.DEVMODE_SUFFIX, "");
-                        PodStatus ps = new PodStatus(name, true, null, 
projectId, environment, true, 
Instant.ofEpochSecond(container.getCreated()).toString());
-                        datagridService.savePodStatus(ps);
-                    }
                 }
             }
         } catch (Exception exception) {
diff --git 
a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerService.java
 
b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerService.java
index eb4c8ef3..7c58465d 100644
--- 
a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerService.java
+++ 
b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerService.java
@@ -5,6 +5,7 @@ import com.github.dockerjava.api.async.ResultCallback;
 import com.github.dockerjava.api.command.CreateContainerResponse;
 import com.github.dockerjava.api.command.CreateNetworkResponse;
 import com.github.dockerjava.api.command.HealthState;
+import com.github.dockerjava.api.command.LogContainerCmd;
 import com.github.dockerjava.api.model.*;
 import com.github.dockerjava.core.DefaultDockerClientConfig;
 import com.github.dockerjava.core.DockerClientConfig;
@@ -16,7 +17,6 @@ import io.quarkus.scheduler.Scheduled;
 import io.smallrye.mutiny.tuples.Tuple2;
 import io.vertx.core.eventbus.EventBus;
 import io.vertx.core.json.JsonObject;
-import org.apache.camel.karavan.bashi.Constants;
 import org.jboss.logging.Logger;
 
 import javax.enterprise.context.ApplicationScoped;
@@ -25,12 +25,14 @@ import java.io.IOException;
 import java.text.DecimalFormat;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 import static 
org.apache.camel.karavan.bashi.ConductorService.ADDRESS_CONTAINER_STATS;
 import static 
org.apache.camel.karavan.bashi.ConductorService.ADDRESS_INFINISPAN_HEALTH;
 import static org.apache.camel.karavan.bashi.Constants.DATAGRID_CONTAINER_NAME;
 import static org.apache.camel.karavan.bashi.Constants.NETWORK_NAME;
+import static 
org.apache.camel.karavan.datagrid.model.DevModeCommand.DEVMODE_SUFFIX;
 
 @ApplicationScoped
 public class DockerService {
@@ -53,7 +55,7 @@ public class DockerService {
             Statistics stats = getContainerStats(container.getId());
 
             String name = container.getNames()[0].replace("/", "");
-            String projectId = name.replace("-" + Constants.DEVMODE_SUFFIX, 
"");
+            String projectId = name.replace(DEVMODE_SUFFIX, "");
             String memoryUsage = 
formatMemory(stats.getMemoryStats().getUsage());
             String memoryLimit = 
formatMemory(stats.getMemoryStats().getLimit());
             JsonObject data = JsonObject.of(
@@ -132,6 +134,11 @@ public class DockerService {
         return containers.get(0);
     }
 
+    public Container getContainerByName(String name) {
+        List<Container> containers = 
getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(name)).exec();
+        return containers.get(0);
+    }
+
     public Statistics getContainerStats(String containerId) {
         InvocationBuilder.AsyncResultCallback<Statistics> callback = new 
InvocationBuilder.AsyncResultCallback<>();
         
getDockerClient().statsCmd(containerId).withContainerId(containerId).withNoStream(true).exec(callback);
@@ -186,6 +193,18 @@ public class DockerService {
         startContainer(name);
     }
 
+    public void logContainer(String containerName) {
+        try {
+            LogCallback callback = new LogCallback();
+            Container container = getContainerByName(containerName);
+            getDockerClient().logContainerCmd(container.getId())
+                
.withStdOut(true).withStdErr(true).withTimestamps(true).exec(callback);
+            callback.awaitCompletion();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     public void stopContainer(String name) {
         List<Container> containers = 
getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(name)).exec();
         if (containers.size() == 1) {
@@ -204,6 +223,14 @@ public class DockerService {
         }
     }
 
+    public void killContainer(String name) {
+        List<Container> containers = 
getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(name)).exec();
+        if (containers.size() == 1) {
+            Container container = containers.get(0);
+            getDockerClient().killContainerCmd(container.getId()).exec();
+        }
+    }
+
     public void pullImage(String image) throws InterruptedException {
         List<Image> images = 
getDockerClient().listImagesCmd().withShowAll(true).exec();
         if (!images.stream().filter(i -> 
Arrays.asList(i.getRepoTags()).contains(image)).findFirst().isPresent()) {
diff --git 
a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/LogCallback.java
 
b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/LogCallback.java
new file mode 100644
index 00000000..48c061cd
--- /dev/null
+++ 
b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/LogCallback.java
@@ -0,0 +1,26 @@
+package org.apache.camel.karavan.bashi.docker;
+
+import com.github.dockerjava.api.async.ResultCallback;
+import com.github.dockerjava.api.model.Frame;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LogCallback extends ResultCallback.Adapter<Frame> {
+    protected final StringBuffer log = new StringBuffer();
+
+    List<Frame> collectedFrames = new ArrayList<>();
+
+    boolean collectFrames = false;
+
+    @Override
+    public void onNext(Frame frame) {
+        if (collectFrames) collectedFrames.add(frame);
+        log.append(new String(frame.getPayload()));
+    }
+
+    @Override
+    public String toString() {
+        return log.toString();
+    }
+}
\ No newline at end of file
diff --git 
a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/DatagridService.java
 
b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/DatagridService.java
index 38f40026..5df37b50 100644
--- 
a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/DatagridService.java
+++ 
b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/DatagridService.java
@@ -73,6 +73,7 @@ public class DatagridService  {
     private RemoteCache<String, Environment> environments;
     private RemoteCache<String, String> commits;
     private RemoteCache<GroupedKey, DevModeStatus> devmodeStatuses;
+    private RemoteCache<GroupedKey, ContainerInfo> containers;
     private RemoteCache<GroupedKey, DevModeCommand> devmodeCommands;
     private final AtomicBoolean ready = new AtomicBoolean(false);
 
@@ -120,6 +121,7 @@ public class DatagridService  {
         commits = getOrCreateCache("commits", false);
         deploymentStatuses = getOrCreateCache(DeploymentStatus.CACHE, false);
         devmodeStatuses = getOrCreateCache(DevModeStatus.CACHE, false);
+        containers = getOrCreateCache(ContainerInfo.CACHE, false);
         devmodeCommands = getOrCreateCache(DevModeCommand.CACHE, true);
 
         cacheManager.getCache(DevModeCommand.CACHE).addClientListener(new 
DevModeCommandListener(eventBus));
@@ -140,15 +142,6 @@ public class DatagridService  {
         return cacheManager.administration().getOrCreateCache(name, new 
StringConfiguration(String.format(config, name)));
     }
 
-    private void cleanData() {
-        environments.clear();
-        deploymentStatuses.clear();
-        podStatuses.clear();
-        pipelineStatuses.clear();
-        camelStatuses.clear();
-        devmodeCommands.clear();
-    }
-
     @ConsumeEvent(value = ADDRESS_DEVMODE_COMMAND_INTERNAL, blocking = true, 
ordered = true, local = false)
     void sendCommand(JsonObject message) {
         GroupedKey key = message.mapTo(GroupedKey.class);
@@ -323,7 +316,7 @@ public class DatagridService  {
 
     public List<CamelStatus> getCamelStatusesByProjectIdEnv(String projectId, 
String env) {
         QueryFactory queryFactory = Search.getQueryFactory(camelStatuses);
-        return queryFactory.<CamelStatus>create("FROM karavan.CamelStatus 
WHERE projectId = :projectId AND name = :env")
+        return queryFactory.<CamelStatus>create("FROM karavan.CamelStatus 
WHERE projectId = :projectId AND env = :env")
                 .setParameter("projectId", projectId)
                 .setParameter("env", env)
                 .execute().list();
@@ -394,19 +387,35 @@ public class DatagridService  {
        return new ArrayList<>(devmodeStatuses.values());
     }
 
-    public void sendDevModeCommand(String projectId, DevModeCommand command) {
-        if (command.getProjectId() == null) {
-            command.setProjectId(projectId);
-        }
-        devmodeCommands.put(GroupedKey.create(projectId, DEFAULT_ENVIRONMENT, 
UUID.randomUUID().toString()), command);
+    public void sendDevModeCommand(DevModeCommand command) {
+        devmodeCommands.put(GroupedKey.create(command.getContainerName(), 
DEFAULT_ENVIRONMENT, command.getTime().toString()), command);
     }
 
     public DevModeCommand getDevModeCommand(GroupedKey key) {
         return devmodeCommands.get(key);
     }
 
-    public DevModeCommand getDevModeCommand(String projectId) {
-        return getDevModeCommand(GroupedKey.create(projectId, 
DEFAULT_ENVIRONMENT, projectId));
+    public void deleteDevModeCommand(DevModeCommand command) {
+        containers.remove(GroupedKey.create(command.getContainerName(), 
DEFAULT_ENVIRONMENT, command.getTime().toString()));
+    }
+
+    public void saveContainerInfo(ContainerInfo ci) {
+        containers.put(GroupedKey.create(ci.getContainerName(), ci.getEnv() != 
null ? ci.getEnv() : DEFAULT_ENVIRONMENT, ci.getContainerName()), ci);
+    }
+
+    public void getContainerInfo(String name, String env) {
+        containers.get(GroupedKey.create(name, env, name));
+    }
+
+    public List<ContainerInfo> getContainerInfos(String env) {
+        QueryFactory queryFactory = Search.getQueryFactory(containers);
+        return queryFactory.<ContainerInfo>create("FROM karavan.ContainerInfo 
WHERE env = :env")
+                .setParameter("env", env)
+                .execute().list();
+    }
+
+    public void deleteContainerInfo(String containerName) {
+        containers.remove(GroupedKey.create(containerName, 
DEFAULT_ENVIRONMENT, containerName));
     }
 
     public void clearAllStatuses() {
@@ -415,7 +424,8 @@ public class DatagridService  {
             podStatuses.clearAsync(),
             pipelineStatuses.clearAsync(),
             camelStatuses.clearAsync(),
-            devmodeCommands.clearAsync()
+            devmodeCommands.clearAsync(),
+            devmodeStatuses.clearAsync()
         ).join();
     }
 
diff --git 
a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/CommandName.java
 
b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/CommandName.java
deleted file mode 100644
index ff34f9ad..00000000
--- 
a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/CommandName.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.apache.camel.karavan.datagrid.model;
-
-import org.infinispan.protostream.annotations.ProtoEnumValue;
-
-public enum CommandName {
-
-        @ProtoEnumValue(number = 0, name = "RUN") RUN,
-        @ProtoEnumValue (number = 1, name = "DELETE") DELETE,
-        @ProtoEnumValue (number = 2, name = "RELOAD") RELOAD
-}
diff --git 
a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/ContainerInfo.java
 
b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/ContainerInfo.java
new file mode 100644
index 00000000..c23dcd16
--- /dev/null
+++ 
b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/ContainerInfo.java
@@ -0,0 +1,70 @@
+package org.apache.camel.karavan.datagrid.model;
+
+import org.infinispan.protostream.annotations.ProtoFactory;
+import org.infinispan.protostream.annotations.ProtoField;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ContainerInfo {
+    public static final String CACHE = "container_infos";
+    @ProtoField(number = 1)
+    String containerName;
+    @ProtoField(number = 2)
+    String containerId;
+    @ProtoField(number = 3)
+    String image;
+    @ProtoField(number = 4, collectionImplementation = ArrayList.class)
+    List<Integer> ports;
+    @ProtoField(number = 5)
+    String env;
+
+    @ProtoFactory
+    public ContainerInfo(String containerName, String containerId, String 
image, List<Integer> ports, String env) {
+        this.containerName = containerName;
+        this.containerId = containerId;
+        this.image = image;
+        this.ports = ports;
+        this.env = env;
+    }
+
+    public String getEnv() {
+        return env;
+    }
+
+    public void setEnv(String env) {
+        this.env = env;
+    }
+
+    public String getContainerName() {
+        return containerName;
+    }
+
+    public void setContainerName(String containerName) {
+        this.containerName = containerName;
+    }
+
+    public String getContainerId() {
+        return containerId;
+    }
+
+    public void setContainerId(String containerId) {
+        this.containerId = containerId;
+    }
+
+    public String getImage() {
+        return image;
+    }
+
+    public void setImage(String image) {
+        this.image = image;
+    }
+
+    public List<Integer> getPorts() {
+        return ports;
+    }
+
+    public void setPorts(List<Integer> ports) {
+        this.ports = ports;
+    }
+}
diff --git 
a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DevModeCommand.java
 
b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DevModeCommand.java
index 1d5a6517..3bc80546 100644
--- 
a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DevModeCommand.java
+++ 
b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DevModeCommand.java
@@ -3,29 +3,46 @@ package org.apache.camel.karavan.datagrid.model;
 import org.infinispan.protostream.annotations.ProtoFactory;
 import org.infinispan.protostream.annotations.ProtoField;
 
+import java.time.Instant;
+
 public class DevModeCommand {
 
     public static final String CACHE = "devmode_commands";
+    public static final String DEVMODE_SUFFIX = "-devmode";
     @ProtoField(number = 1)
-    CommandName commandName;
+    DevModeCommandName commandName;
     @ProtoField(number = 2)
     String projectId;
     @ProtoField(number = 3)
+    String containerName;
+    @ProtoField(number = 4)
+    DevModeCommandType type;
+    @ProtoField(number = 5)
     Long time;
 
     @ProtoFactory
-    public DevModeCommand(CommandName commandName, String projectId, Long 
time) {
+    public DevModeCommand(DevModeCommandName commandName, String projectId, 
String containerName, DevModeCommandType type, Long time) {
         this.commandName = commandName;
         this.projectId = projectId;
+        this.containerName = containerName;
+        this.type = type;
         this.time = time;
     }
 
-    public DevModeCommand(CommandName commandName, Long time) {
-        this.commandName = commandName;
-        this.time = time;
+    public static DevModeCommand createDevModeCommand(DevModeCommandName 
commandName, String projectId) {
+        return new DevModeCommand(commandName, projectId, projectId + 
DEVMODE_SUFFIX, DevModeCommandType.DEVMODE, Instant.now().toEpochMilli());
+    }
+
+    public static DevModeCommand createDevServiceCommand(DevModeCommandName 
commandName, String serviceName) {
+        return new DevModeCommand(commandName, null, serviceName, 
DevModeCommandType.DEVSERVICE, Instant.now().toEpochMilli());
     }
 
-    public DevModeCommand() {
+    public DevModeCommandName getCommandName() {
+        return commandName;
+    }
+
+    public void setCommandName(DevModeCommandName commandName) {
+        this.commandName = commandName;
     }
 
     public String getProjectId() {
@@ -36,12 +53,20 @@ public class DevModeCommand {
         this.projectId = projectId;
     }
 
-    public CommandName getCommandName() {
-        return commandName;
+    public String getContainerName() {
+        return containerName;
     }
 
-    public void setCommandName(CommandName commandName) {
-        this.commandName = commandName;
+    public void setContainerName(String containerName) {
+        this.containerName = containerName;
+    }
+
+    public DevModeCommandType getType() {
+        return type;
+    }
+
+    public void setType(DevModeCommandType type) {
+        this.type = type;
     }
 
     public Long getTime() {
diff --git 
a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DevModeCommandName.java
 
b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DevModeCommandName.java
new file mode 100644
index 00000000..aa0a550b
--- /dev/null
+++ 
b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DevModeCommandName.java
@@ -0,0 +1,12 @@
+package org.apache.camel.karavan.datagrid.model;
+
+import org.infinispan.protostream.annotations.ProtoEnumValue;
+
+public enum DevModeCommandName {
+
+        @ProtoEnumValue(number = 0, name = "RUN") RUN,
+        @ProtoEnumValue (number = 1, name = "STOP") STOP,
+        @ProtoEnumValue (number = 2, name = "DELETE") DELETE,
+        @ProtoEnumValue (number = 3, name = "RELOAD") RELOAD,
+        @ProtoEnumValue (number = 4, name = "LOG") LOG
+}
diff --git 
a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DevModeCommandType.java
 
b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DevModeCommandType.java
new file mode 100644
index 00000000..c0a1d582
--- /dev/null
+++ 
b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DevModeCommandType.java
@@ -0,0 +1,9 @@
+package org.apache.camel.karavan.datagrid.model;
+
+import org.infinispan.protostream.annotations.ProtoEnumValue;
+
+public enum DevModeCommandType {
+
+        @ProtoEnumValue(number = 0, name = "DEVMODE") DEVMODE,
+        @ProtoEnumValue (number = 1, name = "DEVSERVICE") DEVSERVICE
+}
diff --git 
a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/KaravanSchema.java
 
b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/KaravanSchema.java
index be78b42a..22d5e741 100644
--- 
a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/KaravanSchema.java
+++ 
b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/KaravanSchema.java
@@ -14,10 +14,12 @@ import 
org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;
                 PodStatus.class,
                 Environment.class,
                 ServiceStatus.class,
-                CommandName.class,
+                DevModeCommandName.class,
+                DevModeCommandType.class,
                 CamelStatusName.class,
                 DevModeCommand.class,
-                DevModeStatus.class
+                DevModeStatus.class,
+                ContainerInfo.class
         },
         schemaFileName = "karavan.proto",
         schemaFilePath = "proto/",
diff --git 
a/karavan-cloud/karavan-datagrid/src/test/java/org/apache/camel/karavan/datagrid/DataGridTest.java
 
b/karavan-cloud/karavan-datagrid/src/test/java/org/apache/camel/karavan/datagrid/DataGridTest.java
index 5e4fb8c0..e809d178 100644
--- 
a/karavan-cloud/karavan-datagrid/src/test/java/org/apache/camel/karavan/datagrid/DataGridTest.java
+++ 
b/karavan-cloud/karavan-datagrid/src/test/java/org/apache/camel/karavan/datagrid/DataGridTest.java
@@ -10,7 +10,6 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
 
 import javax.inject.Inject;
-import java.time.Instant;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.*;
@@ -38,15 +37,24 @@ public class DataGridTest {
         commandsReceived.add(message.mapTo(DevModeCommand.class));
     }
 
-//    @Test
+    @Test
+    public void testContainersStatuses() throws InterruptedException {
+        ContainerInfo ci = new ContainerInfo("demo", "id", "image", 
List.of(8080, 8081, 8082), "dev");
+        datagridService.saveContainerInfo(ci);
+        List<ContainerInfo> list = datagridService.getContainerInfos("dev");
+        System.out.println(list);
+        assertEquals(1, list.size());
+    }
+
+    @Test
     public void sendCommand() throws InterruptedException {
         List<DevModeCommand> commandsSent = List.of(
-                new DevModeCommand(CommandName.RUN, 
Instant.now().toEpochMilli()),
-                new DevModeCommand(CommandName.RELOAD, 
Instant.now().toEpochMilli()),
-                new DevModeCommand(CommandName.DELETE, 
Instant.now().toEpochMilli()),
-                new DevModeCommand(CommandName.RUN, 
Instant.now().toEpochMilli())
+                DevModeCommand.createDevModeCommand(DevModeCommandName.RUN, 
"test1"),
+                DevModeCommand.createDevModeCommand(DevModeCommandName.RELOAD, 
"test1"),
+                DevModeCommand.createDevModeCommand(DevModeCommandName.DELETE, 
"test1"),
+                DevModeCommand.createDevModeCommand(DevModeCommandName.RUN, 
"test1")
         );
-        commandsSent.forEach(devModeCommand -> 
datagridService.sendDevModeCommand("test1", devModeCommand));
+        commandsSent.forEach(devModeCommand -> 
datagridService.sendDevModeCommand(devModeCommand));
 
         CountDownLatch latch = new CountDownLatch(4);
         latch.await(5, TimeUnit.SECONDS);
diff --git a/karavan-designer/src/designer/beans/BeanProperties.tsx 
b/karavan-designer/src/designer/beans/BeanProperties.tsx
index b7ab5532..24c1e202 100644
--- a/karavan-designer/src/designer/beans/BeanProperties.tsx
+++ b/karavan-designer/src/designer/beans/BeanProperties.tsx
@@ -54,9 +54,9 @@ interface State {
     bean?: NamedBeanDefinition
     properties: Map<string, [string, string, boolean]>
     key: string,
-    showKubernetesSelector: boolean
-    kubernetesSelectorUuid?: string
-    kubernetesSelectorProperty?: string
+    showInfrastructureSelector: boolean
+    infrastructureSelectorUuid?: string
+    infrastructureSelectorProperty?: string
 }
 
 export class BeanProperties extends React.Component<Props, State> {
@@ -70,7 +70,7 @@ export class BeanProperties extends React.Component<Props, 
State> {
     public state: State = {
         bean: this.props.bean,
         key: '',
-        showKubernetesSelector: false,
+        showInfrastructureSelector: false,
         properties: this.props.bean?.properties ? 
this.preparePropertiesMap(this.props.bean?.properties) : new Map<string, 
[string, string, boolean]>()
     };
 
@@ -118,31 +118,31 @@ export class BeanProperties extends 
React.Component<Props, State> {
         })
     }
 
-    selectKubernetes = (value: string) => {
-        const propertyId = this.state.kubernetesSelectorProperty;
-        const uuid = this.state.kubernetesSelectorUuid;
+    selectInfrastructure = (value: string) => {
+        const propertyId = this.state.infrastructureSelectorProperty;
+        const uuid = this.state.infrastructureSelectorUuid;
         if (propertyId && uuid){
             if (value.startsWith("config") || value.startsWith("secret")) 
value = "{{" + value + "}}";
             this.propertyChanged(uuid, propertyId, value, false);
-            this.setState({showKubernetesSelector: false, 
kubernetesSelectorProperty: undefined})
+            this.setState({showInfrastructureSelector: false, 
infrastructureSelectorProperty: undefined})
         }
     }
 
-    openKubernetesSelector = (uuid: string, propertyName: string) => {
-        this.setState({kubernetesSelectorUuid: uuid, 
kubernetesSelectorProperty: propertyName, showKubernetesSelector: true});
+    openInfrastructureSelector = (uuid: string, propertyName: string) => {
+        this.setState({infrastructureSelectorUuid: uuid, 
infrastructureSelectorProperty: propertyName, showInfrastructureSelector: 
true});
     }
 
-    closeKubernetesSelector = () => {
-        this.setState({showKubernetesSelector: false})
+    closeInfrastructureSelector = () => {
+        this.setState({showInfrastructureSelector: false})
     }
 
-    getKubernetesSelectorModal() {
+    getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.showKubernetesSelector}
-                onClose={() => this.closeKubernetesSelector()}
-                onSelect={this.selectKubernetes}/>)
+                isOpen={this.state.showInfrastructureSelector}
+                onClose={() => this.closeInfrastructureSelector()}
+                onSelect={this.selectInfrastructure}/>)
     }
 
     cloneBean = () => {
@@ -208,8 +208,8 @@ export class BeanProperties extends React.Component<Props, 
State> {
                                            onChange={e => 
this.propertyChanged(i, e, value, showPassword)}/>
                                 <InputGroup>
                                     {inInfrastructure &&
-                                        <Tooltip position="bottom-end" 
content="Select value from Kubernetes">
-                                        <Button variant="control" onClick={e 
=> this.openKubernetesSelector(i, key)}>
+                                        <Tooltip position="bottom-end" 
content="Select value from Infrastructure">
+                                        <Button variant="control" onClick={e 
=> this.openInfrastructureSelector(i, key)}>
                                             {icon}
                                         </Button>
                                     </Tooltip>}
@@ -246,7 +246,7 @@ export class BeanProperties extends React.Component<Props, 
State> {
                     {this.state.bean === undefined && <IntegrationHeader 
integration={this.props.integration}/>}
                     {this.state.bean !== undefined && this.getBeanForm()}
                 </Form>
-                {this.getKubernetesSelectorModal()}
+                {this.getInfrastructureSelectorModal()}
             </div>
         )
     }
diff --git 
a/karavan-designer/src/designer/route/property/ComponentParameterField.tsx 
b/karavan-designer/src/designer/route/property/ComponentParameterField.tsx
index 39817f7c..ca1f6a0d 100644
--- a/karavan-designer/src/designer/route/property/ComponentParameterField.tsx
+++ b/karavan-designer/src/designer/route/property/ComponentParameterField.tsx
@@ -23,7 +23,7 @@ import {
     Select,
     SelectVariant,
     SelectDirection,
-    SelectOption, InputGroup, TextArea, Tooltip, Button,
+    SelectOption, InputGroup, TextArea, Tooltip, Button, capitalize,
 } from '@patternfly/react-core';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -57,8 +57,8 @@ interface State {
     selectStatus: Map<string, boolean>
     showEditor: boolean
     showPassword: boolean
-    showKubernetesSelector: boolean
-    kubernetesSelectorProperty?: string
+    showInfrastructureSelector: boolean
+    infrastructureSelectorProperty?: string
     ref: any
     id: string
 }
@@ -69,7 +69,7 @@ export class ComponentParameterField extends 
React.Component<Props, State> {
         selectStatus: new Map<string, boolean>(),
         showEditor: false,
         showPassword: false,
-        showKubernetesSelector: false,
+        showInfrastructureSelector: false,
         ref: React.createRef(),
         id: prefix + "-" + this.props.property.name
     }
@@ -179,7 +179,7 @@ export class ComponentParameterField extends 
React.Component<Props, State> {
         </InputGroup>
     }
 
-    selectKubernetes = (value: string) => {
+    selectInfrastructure = (value: string) => {
         // check if there is a selection
         const textVal = this.state.ref.current;
         const cursorStart = textVal.selectionStart;
@@ -189,29 +189,29 @@ export class ComponentParameterField extends 
React.Component<Props, State> {
             const selectedText = prevValue.substring(cursorStart, cursorEnd)
             value = prevValue.replace(selectedText, value);
         }
-        const propertyName = this.state.kubernetesSelectorProperty;
+        const propertyName = this.state.infrastructureSelectorProperty;
         if (propertyName) {
             if (value.startsWith("config") || value.startsWith("secret")) 
value = "{{" + value + "}}";
             this.parametersChanged(propertyName, value);
-            this.setState({showKubernetesSelector: false, 
kubernetesSelectorProperty: undefined})
+            this.setState({showInfrastructureSelector: false, 
infrastructureSelectorProperty: undefined})
         }
     }
 
-    openKubernetesSelector = (propertyName: string) => {
-        this.setState({kubernetesSelectorProperty: propertyName, 
showKubernetesSelector: true});
+    openInfrastructureSelector = (propertyName: string) => {
+        this.setState({infrastructureSelectorProperty: propertyName, 
showInfrastructureSelector: true});
     }
 
-    closeKubernetesSelector = () => {
-        this.setState({showKubernetesSelector: false})
+    closeInfrastructureSelector = () => {
+        this.setState({showInfrastructureSelector: false})
     }
 
-    getKubernetesSelectorModal() {
+    getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.showKubernetesSelector}
-                onClose={() => this.closeKubernetesSelector()}
-                onSelect={this.selectKubernetes}/>)
+                isOpen={this.state.showInfrastructureSelector}
+                onClose={() => this.closeInfrastructureSelector()}
+                onSelect={this.selectInfrastructure}/>)
     }
 
     getStringInput(property: ComponentProperty, value: any) {
@@ -221,8 +221,8 @@ export class ComponentParameterField extends 
React.Component<Props, State> {
         const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? 
<KubernetesIcon/> : <DockerIcon/>
         return <InputGroup>
             {inInfrastructure && !showEditor && !noInfraSelectorButton &&
-                <Tooltip position="bottom-end" content="Select from 
Kubernetes">
-                    <Button variant="control" onClick={e => 
this.openKubernetesSelector(property.name)}>
+                <Tooltip position="bottom-end" content={"Select from " + 
capitalize((InfrastructureAPI.infrastructure))}>
+                    <Button variant="control" onClick={e => 
this.openInfrastructureSelector(property.name)}>
                         {icon}
                     </Button>
                 </Tooltip>}
@@ -340,7 +340,7 @@ export class ComponentParameterField extends 
React.Component<Props, State> {
                     && this.getSelect(property, value)}
                 {property.type === 'boolean'
                     && this.getSwitch(property, value)}
-                {this.getKubernetesSelectorModal()}
+                {this.getInfrastructureSelectorModal()}
             </FormGroup>
         )
     }
diff --git a/karavan-designer/src/designer/route/property/DslPropertyField.tsx 
b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
index e11ee485..d17a26c3 100644
--- a/karavan-designer/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
@@ -35,7 +35,7 @@ import {
     Text,
     Tooltip,
     Card,
-    InputGroup,
+    InputGroup, capitalize,
 } from '@patternfly/react-core';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -87,8 +87,8 @@ interface State {
     isShowAdvanced: Map<string, boolean>,
     arrayValues: Map<string, string>,
     showEditor: boolean
-    showKubernetesSelector: boolean
-    kubernetesSelectorProperty?: string
+    infrastructureSelector: boolean
+    infrastructureSelectorProperty?: string
     customCode?: string
     ref: any
 }
@@ -100,7 +100,7 @@ export class DslPropertyField extends 
React.Component<Props, State> {
         arrayValues: new Map<string, string>(),
         isShowAdvanced: new Map<string, boolean>(),
         showEditor: false,
-        showKubernetesSelector: false,
+        infrastructureSelector: false,
         ref: React.createRef(),
     };
 
@@ -176,7 +176,7 @@ export class DslPropertyField extends 
React.Component<Props, State> {
         return property.name === 'uri' && !['ToDefinition', 
'ToDynamicDefinition', 'WireTapDefinition'].includes(dslName)
     }
 
-    selectKubernetes = (value: string) => {
+    selectInfrastructure = (value: string) => {
         // check if there is a selection
         const textVal = this.state.ref.current;
         const cursorStart = textVal.selectionStart;
@@ -186,29 +186,29 @@ export class DslPropertyField extends 
React.Component<Props, State> {
             const selectedText = prevValue.substring(cursorStart, cursorEnd)
             value = prevValue.replace(selectedText, value);
         }
-        const propertyName = this.state.kubernetesSelectorProperty;
+        const propertyName = this.state.infrastructureSelectorProperty;
         if (propertyName) {
             if (value.startsWith("config") || value.startsWith("secret")) 
value = "{{" + value + "}}";
             this.propertyChanged(propertyName, value);
-            this.setState({showKubernetesSelector: false, 
kubernetesSelectorProperty: undefined})
+            this.setState({infrastructureSelector: false, 
infrastructureSelectorProperty: undefined})
         }
     }
 
-    openKubernetesSelector = (propertyName: string) => {
-        this.setState({kubernetesSelectorProperty: propertyName, 
showKubernetesSelector: true});
+    openInfrastructureSelector = (propertyName: string) => {
+        this.setState({infrastructureSelectorProperty: propertyName, 
infrastructureSelector: true});
     }
 
-    closeKubernetesSelector = () => {
-        this.setState({showKubernetesSelector: false})
+    closeInfrastructureSelector = () => {
+        this.setState({infrastructureSelector: false})
     }
 
-    getKubernetesSelectorModal() {
+    getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.showKubernetesSelector}
-                onClose={() => this.closeKubernetesSelector()}
-                onSelect={this.selectKubernetes}/>)
+                isOpen={this.state.infrastructureSelector}
+                onClose={() => this.closeInfrastructureSelector()}
+                onSelect={this.selectInfrastructure}/>)
     }
 
     getStringInput = (property: PropertyMeta, value: any) => {
@@ -218,8 +218,8 @@ export class DslPropertyField extends 
React.Component<Props, State> {
         const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? 
<KubernetesIcon/> : <DockerIcon/>
         return (<InputGroup>
             {inInfrastructure && !showEditor && !noInfraSelectorButton &&
-                <Tooltip position="bottom-end" content="Select from 
Kubernetes">
-                    <Button variant="control" onClick={e => 
this.openKubernetesSelector(property.name)}>
+                <Tooltip position="bottom-end" content={"Select from " + 
capitalize(InfrastructureAPI.infrastructure)}>
+                    <Button variant="control" onClick={e => 
this.openInfrastructureSelector(property.name)}>
                         {icon}
                     </Button>
                 </Tooltip>}
@@ -732,7 +732,7 @@ export class DslPropertyField extends 
React.Component<Props, State> {
                     {isKamelet && property.name === 'parameters' && 
this.getKameletParameters()}
                     {!isKamelet && property.name === 'parameters' && 
this.getComponentParameters(property)}
                 </FormGroup>
-                {this.getKubernetesSelectorModal()}
+                {this.getInfrastructureSelectorModal()}
             </div>
         )
     }
diff --git 
a/karavan-designer/src/designer/route/property/InfrastructureSelector.tsx 
b/karavan-designer/src/designer/route/property/InfrastructureSelector.tsx
index 04c8d43a..e0069bc0 100644
--- a/karavan-designer/src/designer/route/property/InfrastructureSelector.tsx
+++ b/karavan-designer/src/designer/route/property/InfrastructureSelector.tsx
@@ -208,6 +208,7 @@ export class InfrastructureSelector extends 
React.Component<Props, State> {
 
     render() {
         const tabIndex = this.state.tabIndex;
+        const tabs = InfrastructureAPI.infrastructure === 'kubernetes' ? 
['configMap', 'secret', 'services'] : ['services'];
         return (
             <Modal
                 aria-label="Select from Infrastructure"
@@ -223,9 +224,7 @@ export class InfrastructureSelector extends 
React.Component<Props, State> {
                         </FlexItem>
                         <FlexItem>
                             <Tabs style={{overflow: 'hidden'}} 
activeKey={this.state.tabIndex} onSelect={this.selectTab}>
-                                <Tab eventKey={"configMap"} key={"configMap"} 
title={<TabTitleText>ConfigMaps</TabTitleText>} />
-                                <Tab eventKey={"secret"} key={"secret"} 
title={<TabTitleText>Secrets</TabTitleText>} />
-                                <Tab eventKey={"service"} key={"service"} 
title={<TabTitleText>Services</TabTitleText>} />
+                                {tabs.map(tab => <Tab eventKey={tab} key={tab} 
title={<TabTitleText>{capitalize(tab)}</TabTitleText>} />)}
                             </Tabs>
                         </FlexItem>
                     </Flex>
@@ -235,7 +234,7 @@ export class InfrastructureSelector extends 
React.Component<Props, State> {
                     {this.searchInput()}
                     {tabIndex === 'configMap' && this.getConfigMapTable()}
                     {tabIndex === 'secret' && this.getSecretsTable()}
-                    {tabIndex === 'service' && this.getServicesTable()}
+                    {tabIndex === 'services' && this.getServicesTable()}
                 </PageSection>
             </Modal>
         )
diff --git 
a/karavan-designer/src/designer/route/property/KameletPropertyField.tsx 
b/karavan-designer/src/designer/route/property/KameletPropertyField.tsx
index 8beed690..9b0a3b39 100644
--- a/karavan-designer/src/designer/route/property/KameletPropertyField.tsx
+++ b/karavan-designer/src/designer/route/property/KameletPropertyField.tsx
@@ -19,7 +19,7 @@ import {
     FormGroup,
     TextInput,
     Popover,
-    Switch, InputGroup, Button, TextArea, Tooltip
+    Switch, InputGroup, Button, TextArea, Tooltip, capitalize
 } from '@patternfly/react-core';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -45,8 +45,8 @@ interface State {
     selectIsOpen: boolean
     showEditor: boolean
     showPassword: boolean
-    showKubernetesSelector: boolean
-    kubernetesSelectorProperty?: string
+    showInfrastructureSelector: boolean
+    infrastructureSelectorProperty?: string
     ref: any
 }
 
@@ -56,7 +56,7 @@ export class KameletPropertyField extends 
React.Component<Props, State> {
         selectIsOpen: false,
         showEditor: false,
         showPassword: false,
-        showKubernetesSelector: false,
+        showInfrastructureSelector: false,
         ref: React.createRef(),
     }
 
@@ -69,7 +69,7 @@ export class KameletPropertyField extends 
React.Component<Props, State> {
         this.setState({selectIsOpen: false});
     }
 
-    selectKubernetes = (value: string) => {
+    selectInfrastructure = (value: string) => {
         // check if there is a selection
         const textVal = this.state.ref.current;
         const cursorStart = textVal.selectionStart;
@@ -79,29 +79,29 @@ export class KameletPropertyField extends 
React.Component<Props, State> {
             const selectedText = prevValue.substring(cursorStart, cursorEnd)
             value = prevValue.replace(selectedText, value);
         }
-        const propertyId = this.state.kubernetesSelectorProperty;
+        const propertyId = this.state.infrastructureSelectorProperty;
         if (propertyId){
             if (value.startsWith("config") || value.startsWith("secret")) 
value = "{{" + value + "}}";
             this.parametersChanged(propertyId, value);
-            this.setState({showKubernetesSelector: false, 
kubernetesSelectorProperty: undefined})
+            this.setState({showInfrastructureSelector: false, 
infrastructureSelectorProperty: undefined})
         }
     }
 
-    openKubernetesSelector = (propertyName: string) => {
-        this.setState({kubernetesSelectorProperty: propertyName, 
showKubernetesSelector: true});
+    openInfrastructureSelector = (propertyName: string) => {
+        this.setState({infrastructureSelectorProperty: propertyName, 
showInfrastructureSelector: true});
     }
 
-    closeKubernetesSelector = () => {
-        this.setState({showKubernetesSelector: false})
+    closeInfrastructureSelector = () => {
+        this.setState({showInfrastructureSelector: false})
     }
 
-    getKubernetesSelectorModal() {
+    getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.showKubernetesSelector}
-                onClose={() => this.closeKubernetesSelector()}
-                onSelect={this.selectKubernetes}/>)
+                isOpen={this.state.showInfrastructureSelector}
+                onClose={() => this.closeInfrastructureSelector()}
+                onSelect={this.selectInfrastructure}/>)
     }
 
     getStringInput() {
@@ -115,8 +115,8 @@ export class KameletPropertyField extends 
React.Component<Props, State> {
         const showInfraSelectorButton = inInfrastructure && !showEditor && 
!noInfraSelectorButton
         return <InputGroup>
             {showInfraSelectorButton  &&
-                <Tooltip position="bottom-end" content="Select from 
Kubernetes">
-                    <Button variant="control" onClick={e => 
this.openKubernetesSelector(property.id)}>
+                <Tooltip position="bottom-end" content={"Select from " + 
capitalize(InfrastructureAPI.infrastructure)}>
+                    <Button variant="control" onClick={e => 
this.openInfrastructureSelector(property.id)}>
                         {icon}
                     </Button>
                 </Tooltip>}
@@ -197,7 +197,7 @@ export class KameletPropertyField extends 
React.Component<Props, State> {
                         onChange={e => this.parametersChanged(property.id, 
!Boolean(value))}/>
                     }
                 </FormGroup>
-                {this.getKubernetesSelectorModal()}
+                {this.getInfrastructureSelectorModal()}
             </div>
         )
     }

Reply via email to