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

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


The following commit(s) were added to refs/heads/main by this push:
     new 7a9c2cb  Saas feature24 (#406)
7a9c2cb is described below

commit 7a9c2cb3e46f24d52d2d920cd05b31b36d705761
Author: Marat Gubaidullin <marat.gubaidul...@gmail.com>
AuthorDate: Fri Jul 1 14:57:10 2022 -0400

    Saas feature24 (#406)
    
    * UI improvements
    
    * Delete pods and deployments
---
 .../camel/karavan/api/KubernetesResource.java      |  27 ++++
 .../apache/camel/karavan/model/KaravanGroup.java   |  35 +++++
 .../apache/camel/karavan/model/KaravanUser.java    |  67 +++++++++
 .../camel/karavan/service/KubernetesService.java   |  22 +++
 .../src/main/resources/application.properties      |   3 +
 karavan-app/src/main/webapp/src/api/KaravanApi.tsx |  20 +++
 karavan-app/src/main/webapp/src/index.css          |  22 ++-
 .../src/main/webapp/src/projects/ProjectHeader.tsx |   5 +-
 .../src/main/webapp/src/projects/ProjectInfo.tsx   | 150 +++++++++++++++------
 .../src/main/webapp/src/projects/ProjectPage.tsx   |  17 ++-
 .../main/webapp/src/projects/PropertiesTable.tsx   |   1 +
 karavan-builder/openshift/karavan-secret.yaml      |   2 +
 12 files changed, 326 insertions(+), 45 deletions(-)

diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/api/KubernetesResource.java
 
b/karavan-app/src/main/java/org/apache/camel/karavan/api/KubernetesResource.java
index fe36476..22ee3a5 100644
--- 
a/karavan-app/src/main/java/org/apache/camel/karavan/api/KubernetesResource.java
+++ 
b/karavan-app/src/main/java/org/apache/camel/karavan/api/KubernetesResource.java
@@ -24,6 +24,7 @@ import org.jboss.logging.Logger;
 
 import javax.inject.Inject;
 import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
@@ -112,4 +113,30 @@ public class KubernetesResource {
         }
         return Response.noContent().build();
     }
+
+    @DELETE
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Path("/deployment/{environment}/{name}")
+    public Response deleteDeployment(@HeaderParam("username") String username, 
@PathParam("environment") String environment, @PathParam("name") String name) 
throws Exception {
+        Optional<KaravanConfiguration.Environment> env = 
configuration.environments().stream().filter(e -> 
e.name().equals(environment)).findFirst();
+        if (env.isPresent()) {
+            kubernetesService.deleteDeployment(name, env.get().namespace());
+            return Response.ok().build();
+        }
+        return Response.noContent().build();
+    }
+
+    @DELETE
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Path("/pod/{environment}/{name}")
+    public Response deletePod(@HeaderParam("username") String username, 
@PathParam("environment") String environment, @PathParam("name") String name) 
throws Exception {
+        Optional<KaravanConfiguration.Environment> env = 
configuration.environments().stream().filter(e -> 
e.name().equals(environment)).findFirst();
+        if (env.isPresent()) {
+            kubernetesService.deletePod(name, env.get().namespace());
+            return Response.ok().build();
+        }
+        return Response.noContent().build();
+    }
 }
\ No newline at end of file
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/model/KaravanGroup.java 
b/karavan-app/src/main/java/org/apache/camel/karavan/model/KaravanGroup.java
new file mode 100644
index 0000000..e871a25
--- /dev/null
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/KaravanGroup.java
@@ -0,0 +1,35 @@
+package org.apache.camel.karavan.model;
+
+import org.infinispan.protostream.annotations.ProtoFactory;
+import org.infinispan.protostream.annotations.ProtoField;
+
+import java.util.List;
+
+public class KaravanGroup {
+    @ProtoField(number = 1)
+    String name;
+    @ProtoField(number = 2)
+    List<String> users;
+
+    @ProtoFactory
+    public KaravanGroup(String name, List<String> users) {
+        this.name = name;
+        this.users = users;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public List<String> getUsers() {
+        return users;
+    }
+
+    public void setUsers(List<String> users) {
+        this.users = users;
+    }
+}
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/model/KaravanUser.java 
b/karavan-app/src/main/java/org/apache/camel/karavan/model/KaravanUser.java
new file mode 100644
index 0000000..d116ad8
--- /dev/null
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/KaravanUser.java
@@ -0,0 +1,67 @@
+package org.apache.camel.karavan.model;
+
+import org.infinispan.protostream.annotations.ProtoFactory;
+import org.infinispan.protostream.annotations.ProtoField;
+
+public class KaravanUser {
+    @ProtoField(number = 1)
+    String username;
+    @ProtoField(number = 2)
+    String password;
+    @ProtoField(number = 3)
+    String firstName;
+    @ProtoField(number = 4)
+    String lastName;
+    @ProtoField(number = 5)
+    boolean showTour;
+
+
+    @ProtoFactory
+    public KaravanUser(String username, String password, String firstName, 
String lastName, boolean showTour) {
+        this.username = username;
+        this.password = password;
+        this.firstName = firstName;
+        this.lastName = lastName;
+        this.showTour = showTour;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+
+    public boolean isShowTour() {
+        return showTour;
+    }
+
+    public void setShowTour(boolean showTour) {
+        this.showTour = showTour;
+    }
+}
diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java
 
b/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java
index dfcda42..23a868e 100644
--- 
a/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java
+++ 
b/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java
@@ -20,6 +20,8 @@ import io.fabric8.kubernetes.api.model.ObjectMeta;
 import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
 import io.fabric8.kubernetes.api.model.Pod;
 import io.fabric8.kubernetes.api.model.Secret;
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.ConfigBuilder;
 import io.fabric8.kubernetes.client.DefaultKubernetesClient;
 import io.fabric8.kubernetes.client.KubernetesClient;
 import io.fabric8.openshift.api.model.DeploymentConfig;
@@ -32,6 +34,7 @@ import io.fabric8.tekton.pipeline.v1beta1.PipelineRun;
 import io.fabric8.tekton.pipeline.v1beta1.PipelineRunBuilder;
 import io.fabric8.tekton.pipeline.v1beta1.PipelineRunSpec;
 import io.fabric8.tekton.pipeline.v1beta1.PipelineRunSpecBuilder;
+import io.quarkus.kubernetes.client.runtime.KubernetesClientBuildConfig;
 import io.smallrye.mutiny.tuples.Tuple2;
 import io.smallrye.mutiny.tuples.Tuple3;
 import org.apache.camel.karavan.model.DeploymentStatus;
@@ -150,6 +153,25 @@ public class KubernetesService {
         }
     }
 
+    public void deleteDeployment(String name, String namespace) {
+        try {
+            if (kubernetesClient().isAdaptable(OpenShiftClient.class)) {
+                
openshiftClient().deploymentConfigs().inNamespace(namespace).withName(name).delete();
+            } else {
+                // TODO: Implement Deployment for Kubernetes/Minikube
+            }
+        } catch (Exception ex) {
+            LOGGER.error(ex.getMessage());
+        }
+    }
+
+    public void deletePod(String name, String namespace) {
+        try {
+            
kubernetesClient().pods().inNamespace(namespace).withName(name).delete();
+        } catch (Exception ex) {
+            LOGGER.error(ex.getMessage());
+        }
+    }
 
     public DeploymentStatus getDeploymentStatus(String name, String namespace) 
{
         try {
diff --git a/karavan-app/src/main/resources/application.properties 
b/karavan-app/src/main/resources/application.properties
index 0c0680a..4808ab5 100644
--- a/karavan-app/src/main/resources/application.properties
+++ b/karavan-app/src/main/resources/application.properties
@@ -67,6 +67,9 @@ quarkus.kubernetes.deployment-target=openshift
 quarkus.kubernetes-config.enabled=true
 quarkus.kubernetes-config.secrets.enabled=true
 
+quarkus.kubernetes-client.connection-timeout=2000
+quarkus.kubernetes-client.request-timeout=10000
+
 quarkus.openshift.route.expose=true
 quarkus.openshift.name=karavan
 quarkus.openshift.namespace=karavan
diff --git a/karavan-app/src/main/webapp/src/api/KaravanApi.tsx 
b/karavan-app/src/main/webapp/src/api/KaravanApi.tsx
index b834fd7..b1868e7 100644
--- a/karavan-app/src/main/webapp/src/api/KaravanApi.tsx
+++ b/karavan-app/src/main/webapp/src/api/KaravanApi.tsx
@@ -167,6 +167,26 @@ export const KaravanApi = {
         });
     },
 
+    deleteDeployment: async (environment: string, name: string, after: (res: 
AxiosResponse<any>) => void) => {
+        axios.delete('/kubernetes/deployment/' + environment + '/' + name,
+            {headers: {'Accept': 'application/json', 'Content-Type': 
'application/json', 'username': 'cameleer'}})
+            .then(res => {
+                after(res);
+            }).catch(err => {
+            after(err);
+        });
+    },
+
+    deletePod: async (environment: string, name: string, after: (res: 
AxiosResponse<any>) => void) => {
+        axios.delete('/kubernetes/pod/' + environment + '/' + name,
+            {headers: {'Accept': 'application/json', 'Content-Type': 
'application/json', 'username': 'cameleer'}})
+            .then(res => {
+                after(res);
+            }).catch(err => {
+            after(err);
+        });
+    },
+
     getKameletNames: async (after: (names: []) => void) => {
         axios.get('/kamelet',
             {headers: {'Accept': 'application/json'}})
diff --git a/karavan-app/src/main/webapp/src/index.css 
b/karavan-app/src/main/webapp/src/index.css
index c95c4b9..1378930 100644
--- a/karavan-app/src/main/webapp/src/index.css
+++ b/karavan-app/src/main/webapp/src/index.css
@@ -13,6 +13,7 @@
 .karavan .pf-c-page__main {
   overflow: hidden;
 }
+
 .logo,
 .logo svg {
   height: 48px;
@@ -90,18 +91,37 @@
   font-weight: initial;
 }
 
+/*Projects*/
+
+.karavan .tools .pf-c-button {
+  font-size: 14px;
+}
+
 .karavan .projects-page .icon {
   height: 16px;
   width: 16px;
   margin: auto;
 }
 
+.karavan .projects-page .pf-m-link {
+  font-size: 14px;
+}
+
 .karavan .project-page .project-page-section {
   background-color: white;
 }
 
-.karavan .project-page .project-details {
+.karavan .project-page .pf-c-description-list__group {
+  min-height: 30px;
+}
+
+.karavan .project-page .pf-c-tabs__link,
+.karavan .project-page .pf-m-link {
+  font-size: 14px;
+}
 
+.karavan .project-page .pf-c-description-list__text {
+  font-size: 15px;
 }
 
 .karavan .project-page .table {
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectHeader.tsx 
b/karavan-app/src/main/webapp/src/projects/ProjectHeader.tsx
index 3e26968..2391c0e 100644
--- a/karavan-app/src/main/webapp/src/projects/ProjectHeader.tsx
+++ b/karavan-app/src/main/webapp/src/projects/ProjectHeader.tsx
@@ -7,7 +7,7 @@ import {
     PageSection,
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
-import {Project, ProjectFile, ProjectStatus} from "../models/ProjectModels";
+import {Project, ProjectStatus} from "../models/ProjectModels";
 import {ProjectDashboard} from "./ProjectDashboard";
 import {ProjectInfo} from "./ProjectInfo";
 
@@ -15,6 +15,7 @@ interface Props {
     project: Project,
     config: any,
     showLog: (type: 'container' | 'pipeline', name: string, environment: 
string) => void
+    deleteEntity: (type: 'pod' | 'deployment', name: string, environment: 
string) => void
 }
 
 interface State {
@@ -42,7 +43,7 @@ export class ProjectHeader extends React.Component<Props, 
State> {
                 </FlexItem>
                 <FlexItem>
                     <PageSection padding={{default: "padding"}}>
-                        {tab === 'details' && <ProjectInfo 
project={this.props.project} config={this.props.config} 
showLog={this.props.showLog}/>}
+                        {tab === 'details' && <ProjectInfo 
project={this.props.project} config={this.props.config} 
deleteEntity={this.props.deleteEntity} showLog={this.props.showLog}/>}
                         {tab === 'dashboard' && <ProjectDashboard 
project={this.props.project} config={this.props.config}/>}
                     </PageSection>
                 </FlexItem>
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx 
b/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx
index 97737c8..cbc4820 100644
--- a/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx
+++ b/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx
@@ -8,7 +8,7 @@ import {
     DescriptionListGroup,
     DescriptionListDescription,
     Card,
-    CardBody, Spinner, Tooltip, Flex, FlexItem, Divider, LabelGroup, Label
+    CardBody, Spinner, Tooltip, Flex, FlexItem, Divider, LabelGroup, Label, 
Modal
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
 import {KaravanApi} from "../api/KaravanApi";
@@ -19,11 +19,13 @@ import PushIcon from 
"@patternfly/react-icons/dist/esm/icons/code-branch-icon";
 import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
 import DownIcon from 
"@patternfly/react-icons/dist/esm/icons/error-circle-o-icon";
 import ClockIcon from "@patternfly/react-icons/dist/esm/icons/clock-icon";
+import DeleteIcon from 
"@patternfly/react-icons/dist/esm/icons/times-circle-icon";
 
 interface Props {
     project: Project,
     config: any,
     showLog: (type: 'container' | 'pipeline', name: string, environment: 
string) => void
+    deleteEntity: (type: 'pod' | 'deployment', name: string, environment: 
string) => void
 }
 
 interface State {
@@ -32,6 +34,10 @@ interface State {
     isPushing: boolean,
     isBuilding: boolean,
     isRolling: boolean,
+    showDeleteConfirmation: boolean,
+    deleteEntity?: 'pod' | 'deployment',
+    deleteEntityName?: string,
+    deleteEntityEnv?: string,
     environments: string[],
     environment: string,
     key?: string,
@@ -44,6 +50,7 @@ export class ProjectInfo extends React.Component<Props, 
State> {
         isPushing: false,
         isBuilding: false,
         isRolling: false,
+        showDeleteConfirmation: false,
         environments: this.props.config.environments && 
Array.isArray(this.props.config.environments)
             ? Array.from(this.props.config.environments) : [],
         environment: this.props.config.environments && 
Array.isArray(this.props.config.environments)
@@ -160,6 +167,22 @@ export class ProjectInfo extends React.Component<Props, 
State> {
         </Tooltip>)
     }
 
+    deleteDeploymentButton = (env: string) => {
+        return (<Tooltip content="Delete deployment" position={"left"}>
+            <Button isSmall variant="secondary"
+                    className="project-button"
+                    icon={<DeleteIcon/>}
+                    onClick={e => this.setState({
+                        showDeleteConfirmation: true,
+                        deleteEntity: "deployment",
+                        deleteEntityEnv: env,
+                        deleteEntityName: this.state.project?.projectId
+                    })}>
+                {"Delete"}
+            </Button>
+        </Tooltip>)
+    }
+
     getCommitPanel() {
         const {project} = this.state;
         return (
@@ -187,9 +210,9 @@ export class ProjectInfo extends React.Component<Props, 
State> {
                     </DescriptionListDescription>
                 </DescriptionListGroup>
                 <DescriptionListGroup>
-                    <DescriptionListTerm>Replicas</DescriptionListTerm>
+                    <DescriptionListTerm>Deployment</DescriptionListTerm>
                     <DescriptionListDescription>
-                        {deploymentStatus && 
this.getReplicasPanel(deploymentStatus)}
+                        {deploymentStatus && 
this.getReplicasPanel(deploymentStatus, env)}
                     </DescriptionListDescription>
                 </DescriptionListGroup>
                 <DescriptionListGroup>
@@ -207,50 +230,76 @@ export class ProjectInfo extends React.Component<Props, 
State> {
             </DescriptionList>)
     }
 
-    getReplicasPanel(deploymentStatus: DeploymentStatus) {
+    getReplicasPanel(deploymentStatus: DeploymentStatus, env: string) {
         const ok = (deploymentStatus && deploymentStatus?.readyReplicas > 0
             && deploymentStatus.unavailableReplicas === 0
             && deploymentStatus?.replicas === deploymentStatus?.readyReplicas)
         return (
+            <Flex justifyContent={{default: "justifyContentSpaceBetween"}} 
alignItems={{default: "alignItemsCenter"}}>
+                <FlexItem>
             <LabelGroup numLabels={3}>
                 <Tooltip content={"Ready Replicas / Replicas"} 
position={"left"}>
-                    <Label icon={<UpIcon/>}
-                           color={ok ? "green" : 
"grey"}>{deploymentStatus.readyReplicas + " / " + 
deploymentStatus.replicas}</Label>
+                    <Label icon={ok ? <UpIcon/> : <DownIcon/>}
+                           color={ok ? "green" : "grey"}>{"Replicas: " + 
deploymentStatus.readyReplicas + " / " + deploymentStatus.replicas}</Label>
                 </Tooltip>
                 {deploymentStatus.unavailableReplicas > 0 &&
                     <Tooltip content={"Unavailable replicas"} 
position={"right"}>
-                        <Label icon={<UpIcon/>} 
color={"red"}>{deploymentStatus.unavailableReplicas}</Label>
+                        <Label icon={<DownIcon/>} 
color={"red"}>{deploymentStatus.unavailableReplicas}</Label>
                     </Tooltip>
                 }
             </LabelGroup>
+                </FlexItem>
+                <FlexItem>{env === "dev" && 
this.deleteDeploymentButton(env)}</FlexItem>
+            </Flex>
         )
     }
 
     getPodsPanel(deploymentStatus: DeploymentStatus, env: string) {
         const podStatuses = deploymentStatus.podStatuses;
         return (
-        <Flex justifyContent={{default: "justifyContentSpaceBetween"}} 
alignItems={{default: "alignItemsCenter"}}>
-            <FlexItem>
-                <LabelGroup numLabels={3}>
-                    {podStatuses.map(pod => {
-                        const running = pod.started && pod.ready;
-                        return (
-                            <Tooltip content={running ? "Running" : 
pod.reason}>
-                                <Label icon={running ? <UpIcon/> : 
<DownIcon/>} color={running ? "green" : "red"} >
-                                    <Button variant="link" onClick={e => 
this.props.showLog?.call(this, 'container', pod.name, env)}>
-                                        {pod.name}
-                                    </Button>
-                                </Label>
-                            </Tooltip>
+            <Flex justifyContent={{default: "justifyContentSpaceBetween"}} 
alignItems={{default: "alignItemsCenter"}}>
+                <FlexItem>
+                    <LabelGroup numLabels={3}>
+                        {(podStatuses === undefined || podStatuses.length === 
0) && <Label icon={<DownIcon/>} color={"grey"}>No pods</Label>}
+                        {podStatuses.map(pod => {
+                                const running = pod.started && pod.ready;
+                                return (
+                                    <Tooltip content={running ? "Running" : 
pod.reason}>
+                                        <Label icon={running ? <UpIcon/> : 
<DownIcon/>} color={running ? "green" : "red"}>
+                                            <Button variant="link"
+                                                    onClick={e => 
this.props.showLog?.call(this, 'container', pod.name, env)}>
+                                                {pod.name}
+                                            </Button>
+                                            <Tooltip content={"Delete Pod"}>
+                                                <Button icon={<DeleteIcon/>} 
variant="link" onClick={e => this.setState({
+                                                    showDeleteConfirmation: 
true,
+                                                    deleteEntity: "pod",
+                                                    deleteEntityEnv: env,
+                                                    deleteEntityName: pod.name
+                                                })}></Button>
+                                            </Tooltip>
+                                        </Label>
+                                    </Tooltip>
+                                )
+                            }
                         )}
-                    )}
-                </LabelGroup>
-            </FlexItem>
-            <FlexItem>{env === "dev" && this.rolloutButton()}</FlexItem>
-        </Flex>
+                    </LabelGroup>
+                </FlexItem>
+                <FlexItem>{env === "dev" && this.rolloutButton()}</FlexItem>
+            </Flex>
         )
     }
 
+    getStatusColor(status?: string) {
+        if (status === 'UP') return 'green';
+        if (status === 'DOWN') return 'red';
+        if (status === 'NA') return 'blue';
+    }
+
+    getStatusIcon(status?: string) {
+        return (status === 'UP' ? <UpIcon/> : <DownIcon/>)
+    }
+
     getHealthPanel(env: string) {
         const status = this.state.status?.statuses.find(s => s.environment === 
env)
         const registryStatus = status?.registryStatus;
@@ -260,11 +309,13 @@ export class ProjectInfo extends React.Component<Props, 
State> {
         const contextVersion = status?.contextVersion;
         return (
             <LabelGroup numLabels={5}>
-                {contextVersion && <Label icon={<UpIcon/>} 
color={contextStatus === "UP" ? "green" : "grey"}>{contextVersion}</Label>}
-                <Label icon={<UpIcon/>} color={contextStatus === "UP" ? 
"green" : "grey"}>Context</Label>
-                <Label icon={<UpIcon/>} color={consumersStatus === "UP" ? 
"green" : "grey"}>Consumers</Label>
-                <Label icon={<UpIcon/>} color={routesStatus === "UP" ? "green" 
: "grey"}>Routes</Label>
-                <Label icon={<UpIcon/>} color={registryStatus === "UP" ? 
"green" : "grey"}>Registry</Label>
+                {contextVersion &&
+                    <Label icon={this.getStatusIcon(contextStatus)} 
color={this.getStatusColor(contextStatus)}>{contextVersion}</Label>}
+                <Label icon={this.getStatusIcon(contextStatus)} 
color={this.getStatusColor(contextStatus)}>Context</Label>
+                <Label icon={this.getStatusIcon(consumersStatus)} 
color={this.getStatusColor(consumersStatus)}>Consumers</Label>
+                <Label icon={this.getStatusIcon(routesStatus)} 
color={this.getStatusColor(routesStatus)}>Routes</Label>
+                {registryStatus !== 'NA' &&
+                    <Label icon={this.getStatusIcon(registryStatus)} 
color={this.getStatusColor(registryStatus)}>Registry</Label>}
             </LabelGroup>
         )
     }
@@ -274,24 +325,25 @@ export class ProjectInfo extends React.Component<Props, 
State> {
         const pipeline = status?.lastPipelineRun;
         const pipelineResult = status?.lastPipelineRunResult;
         const lastPipelineRunTime = status?.lastPipelineRunTime;
+        const showTime = lastPipelineRunTime && lastPipelineRunTime > 0;
         const isRunning = pipelineResult === 'Running';
         const isFailed = pipelineResult === 'Failed';
         const isSucceeded = pipelineResult === 'Succeeded';
         const color = isSucceeded ? "green" : (isFailed ? "red" : (isRunning ? 
"blue" : "grey"))
+        const icon = isSucceeded ? <UpIcon/> : <DownIcon/>
         return (
             <Flex justifyContent={{default: "justifyContentSpaceBetween"}} 
alignItems={{default: "alignItemsCenter"}}>
                 <FlexItem>
                     <Tooltip content={pipelineResult} position={"right"}>
                         <LabelGroup numLabels={2}>
-                            <Label icon={isRunning ? <Spinner isSVG 
diameter="16px"/> : <UpIcon/>} color={color}>
+                            <Label icon={isRunning ? <Spinner isSVG 
diameter="16px"/> : icon} color={color}>
                                 <Button variant="link" onClick={e => {
                                     if (pipeline) 
this.props.showLog?.call(this, 'pipeline', pipeline, env);
                                 }}>
                                     {pipeline ? pipeline : "-"}
                                 </Button>
                             </Label>
-                            {lastPipelineRunTime && lastPipelineRunTime > 0 &&
-                                <Label icon={<ClockIcon/>} 
color={color}>{lastPipelineRunTime + "s"}</Label>}
+                            {showTime && <Label icon={<ClockIcon/>} 
color={color}>{lastPipelineRunTime + "s"}</Label>}
                         </LabelGroup>
                     </Tooltip>
                 </FlexItem>
@@ -300,14 +352,6 @@ export class ProjectInfo extends React.Component<Props, 
State> {
         )
     }
 
-    isUp(env: string): boolean {
-        if (this.state.status) {
-            return this.state.status.statuses.find(s => s.environment === 
env)?.status === 'UP';
-        } else {
-            return false;
-        }
-    }
-
     getProjectDescription() {
         const {project} = this.state;
         return (<DescriptionList isHorizontal>
@@ -332,6 +376,29 @@ export class ProjectInfo extends React.Component<Props, 
State> {
         </DescriptionList>)
     }
 
+    getDeleteConfirmation() {
+        const {deleteEntity, deleteEntityEnv, deleteEntityName} = this.state;
+        return (<Modal
+            className="modal-delete"
+            title="Confirmation"
+            isOpen={this.state.showDeleteConfirmation}
+            onClose={() => this.setState({showDeleteConfirmation: false})}
+            actions={[
+                <Button key="confirm" variant="primary" onClick={e => {
+                    if (deleteEntityEnv && deleteEntityName && deleteEntity) {
+                        this.props.deleteEntity?.call(this, deleteEntity, 
deleteEntityName, deleteEntityEnv);
+                        this.setState({showDeleteConfirmation: false});
+                    }
+                }}>Delete
+                </Button>,
+                <Button key="cancel" variant="link"
+                        onClick={e => this.setState({showDeleteConfirmation: 
false})}>Cancel</Button>
+            ]}
+            onEscapePress={e => this.setState({showDeleteConfirmation: 
false})}>
+            <div>{"Delete " + deleteEntity + " " + deleteEntityName + 
"?"}</div>
+        </Modal>)
+    }
+
     render() {
         return (
             <Card>
@@ -348,6 +415,7 @@ export class ProjectInfo extends React.Component<Props, 
State> {
                         </FlexItem>
                     </Flex>
                 </CardBody>
+                {this.state.showDeleteConfirmation && 
this.getDeleteConfirmation()}
             </Card>
         )
     }
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx 
b/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx
index d9386f7..3c9e0c2 100644
--- a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx
+++ b/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx
@@ -318,6 +318,21 @@ export class ProjectPage extends React.Component<Props, 
State> {
 
     }
 
+    deleteEntity = (type: 'pod' | 'deployment', name: string, environment: 
string) => {
+        switch (type) {
+            case "deployment": KaravanApi.deleteDeployment(environment, name, 
(res: any) => {
+                if (Array.isArray(res) && Array.from(res).length > 0)
+                    this.onRefresh();
+            });
+                break;
+            case "pod": KaravanApi.deletePod(environment, name, (res: any) => {
+                if (Array.isArray(res) && Array.from(res).length > 0)
+                    this.onRefresh();
+            });
+                break;
+        }
+    }
+
     getLogView = () => {
         const file = this.state.file;
         return (
@@ -371,7 +386,7 @@ export class ProjectPage extends React.Component<Props, 
State> {
                 {file === undefined &&
                     <PageSection isFilled className="kamelets-page 
project-page-section"
                                  padding={{default: file !== undefined ? 
'noPadding' : 'noPadding'}}>
-                        {<ProjectHeader project={this.props.project} 
config={this.props.config} showLog={this.showPipelineLog}/>}
+                        {<ProjectHeader project={this.props.project} 
config={this.props.config} showLog={this.showPipelineLog} 
deleteEntity={this.deleteEntity}/>}
                         {this.getProjectFiles()}
                     </PageSection>}
                 {showDesigner && this.getDesigner()}
diff --git a/karavan-app/src/main/webapp/src/projects/PropertiesTable.tsx 
b/karavan-app/src/main/webapp/src/projects/PropertiesTable.tsx
index 199fde7..eaa5b23 100644
--- a/karavan-app/src/main/webapp/src/projects/PropertiesTable.tsx
+++ b/karavan-app/src/main/webapp/src/projects/PropertiesTable.tsx
@@ -129,6 +129,7 @@ export class PropertiesTable extends React.Component<Props, 
State> {
                                 )})}
                         </Tbody>
                     </TableComposable>}
+                {this.state.showDeleteConfirmation && 
this.getDeleteConfirmation()}
                 <Panel>
                     <PanelMain>
                         <PanelMainBody>
diff --git a/karavan-builder/openshift/karavan-secret.yaml 
b/karavan-builder/openshift/karavan-secret.yaml
index 948ed26..ac5a3a6 100644
--- a/karavan-builder/openshift/karavan-secret.yaml
+++ b/karavan-builder/openshift/karavan-secret.yaml
@@ -4,6 +4,8 @@ metadata:
   name: karavan
 type: Opaque
 stringData:
+  master-user: admin
+  master-password: karavan
   git-repository: https://github.com/mgubaidullin/karavan-demo.git
   git-password: demo
   git-username: demo

Reply via email to