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