You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2023/08/29 00:00:03 UTC

[camel-karavan] branch main updated: Forgotten files added

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 78a6de87 Forgotten files added
78a6de87 is described below

commit 78a6de87b9e042c6e2060bc4bca50463b7d2baaa
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Aug 28 19:59:55 2023 -0400

    Forgotten files added
---
 .../main/webui/src/project/build/BuildStatus.tsx   | 308 +++++++++++++++++++++
 .../webui/src/project/build/ProjectBuildTab.tsx    |  23 ++
 2 files changed, 331 insertions(+)

diff --git a/karavan-web/karavan-app/src/main/webui/src/project/build/BuildStatus.tsx b/karavan-web/karavan-app/src/main/webui/src/project/build/BuildStatus.tsx
new file mode 100644
index 00000000..c926c257
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/webui/src/project/build/BuildStatus.tsx
@@ -0,0 +1,308 @@
+import React, {useState} from 'react';
+import {
+    Button,
+    DescriptionList,
+    DescriptionListTerm,
+    DescriptionListGroup,
+    DescriptionListDescription, Spinner, Tooltip, Flex, FlexItem, LabelGroup, Label, Modal, Badge, CardBody, Card
+} from '@patternfly/react-core';
+import '../../designer/karavan.css';
+import {KaravanApi} from "../../api/KaravanApi";
+import BuildIcon from "@patternfly/react-icons/dist/esm/icons/build-icon";
+import RolloutIcon from "@patternfly/react-icons/dist/esm/icons/process-automation-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";
+import {ContainerStatus} from "../../api/ProjectModels";
+import {useLogStore, useProjectStore, useStatusesStore} from "../../api/ProjectStore";
+import {shallow} from "zustand/shallow";
+
+interface Props {
+    env: string,
+}
+
+export function BuildStatus (props: Props) {
+
+    const [project] = useProjectStore((s) => [s.project], shallow);
+    const [containers, deployments, camels, pipelineStatuses] =
+        useStatusesStore((s) => [s.containers, s.deployments, s.camels, s.pipelineStatuses], shallow);
+    const [isPushing, setIsPushing] = useState<boolean>(false);
+    const [isBuilding, setIsBuilding] = useState<boolean>(false);
+    const [isRolling, setIsRolling] = useState<boolean>(false);
+    const [showDeleteConfirmation, setShowDeleteConfirmation] = useState<boolean>(false);
+    const [deleteEntityType, setDeleteEntityType] = useState<'pod' | 'deployment' | 'pipelinerun'>('pod');
+    const [deleteEntityName, setDeleteEntityName] = useState<string>();
+    const [deleteEntityEnv, setDeleteEntityEnv] = useState<string>();
+
+    function deleteEntity(type: 'pod' | 'deployment' | 'pipelinerun', name: string, environment: string) {
+        switch (type) {
+            case "deployment":
+                KaravanApi.deleteDeployment(environment, name, (res: any) => {
+                    // if (Array.isArray(res) && Array.from(res).length > 0)
+                    // onRefresh();
+                });
+                break;
+            case "pod":
+                KaravanApi.deleteContainer(environment, 'project', name, (res: any) => {
+                    // if (Array.isArray(res) && Array.from(res).length > 0)
+                    // onRefresh();
+                });
+                break;
+            case "pipelinerun":
+                KaravanApi.stopPipelineRun(environment, name, (res: any) => {
+                    // if (Array.isArray(res) && Array.from(res).length > 0)
+                    // onRefresh();
+                });
+                break;
+        }
+    }
+
+    function build() {
+        setIsBuilding(true);
+        KaravanApi.pipelineRun(project, env, res => {
+            if (res.status === 200 || res.status === 201) {
+                setIsBuilding(false);
+            } else {
+                // Todo notification
+            }
+        });
+    }
+
+    function rollout() {
+        setIsRolling(true);
+        KaravanApi.rolloutDeployment(project.projectId, env, res => {
+            console.log(res)
+            if (res.status === 200 || res.status === 201) {
+                setIsRolling(false);
+            } else {
+                // Todo notification
+            }
+        });
+    }
+
+    function buildButton(env: string) {
+        const status = pipelineStatuses.filter(p => p.projectId === project.projectId).at(0);
+        const isRunning = status?.result === 'Running';
+        return (<Tooltip content="Start build pipeline" position={"left"}>
+            <Button isLoading={isBuilding ? true : undefined}
+                    isDisabled={isBuilding || isRunning || isPushing}
+                    size="sm"
+                    variant="secondary"
+                    className="project-button"
+                    icon={!isBuilding ? <BuildIcon/> : <div></div>}
+                    onClick={e => build()}>
+                {isBuilding ? "..." : "Build"}
+            </Button>
+        </Tooltip>)
+    }
+
+    function rolloutButton() {
+        return (<Tooltip content="Rollout deployment" position={"left"}>
+            <Button isLoading={isRolling ? true : undefined} size="sm" variant="secondary"
+                    className="project-button"
+                    icon={!isRolling ? <RolloutIcon/> : <div></div>}
+                    onClick={e => rollout()}>
+                {isRolling ? "..." : "Rollout"}
+            </Button>
+        </Tooltip>)
+    }
+
+    function deleteDeploymentButton(env: string) {
+        return (<Tooltip content="Delete deployment" position={"left"}>
+            <Button size="sm" variant="secondary"
+                    className="project-button"
+                    icon={<DeleteIcon/>}
+                    onClick={e => {
+                        setShowDeleteConfirmation(true);
+                        setDeleteEntityType("deployment");
+                        setDeleteEntityEnv(env);
+                        setDeleteEntityName(project?.projectId);
+                    }}>
+                {"Delete"}
+            </Button>
+        </Tooltip>)
+    }
+
+    function getReplicasPanel(env: string) {
+        const deploymentStatus = deployments.find(d => d.name === project?.projectId);
+        const ok = (deploymentStatus && deploymentStatus?.readyReplicas > 0
+            && (deploymentStatus.unavailableReplicas === 0 || deploymentStatus.unavailableReplicas === undefined || deploymentStatus.unavailableReplicas === null)
+            && deploymentStatus?.replicas === deploymentStatus?.readyReplicas)
+        return (
+            <Flex justifyContent={{default: "justifyContentSpaceBetween"}} alignItems={{default: "alignItemsCenter"}}>
+                <FlexItem>
+                    {deploymentStatus && <LabelGroup numLabels={3}>
+                        <Tooltip content={"Ready Replicas / Replicas"} position={"left"}>
+                            <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={<DownIcon/>} color={"red"}>{deploymentStatus.unavailableReplicas}</Label>
+                            </Tooltip>
+                        }
+                    </LabelGroup>}
+                    {deploymentStatus === undefined && <Label icon={<DownIcon/>} color={"grey"}>No deployments</Label>}
+                </FlexItem>
+                <FlexItem>{env === "dev" && deleteDeploymentButton(env)}</FlexItem>
+            </Flex>
+        )
+    }
+
+    function getPodsPanel(env: string) {
+        const podStatuses = containers.filter(d => d.projectId === project?.projectId);
+        return (
+            <Flex justifyContent={{default: "justifyContentSpaceBetween"}}
+                  alignItems={{default: "alignItemsFlexStart"}}>
+                <FlexItem>
+                    {podStatuses.length === 0 && <Label icon={<DownIcon/>} color={"grey"}>No pods</Label>}
+                    <LabelGroup numLabels={2} isVertical>
+                        {podStatuses.map((pod: ContainerStatus) => {
+                                const ready = pod.state === 'running';
+                                return (
+                                    <Tooltip key={pod.containerName} content={pod.state}>
+                                        <Label icon={ready ? <UpIcon/> : <DownIcon/>} color={ready ? "green" : "red"}>
+                                            <Button variant="link" className="labeled-button"
+                                                    onClick={e => {
+                                                        useLogStore.setState({
+                                                            showLog: true,
+                                                            type: 'container',
+                                                            podName: pod.containerName
+                                                        });
+                                                    }}>
+                                                {pod.containerName}
+                                            </Button>
+                                            <Tooltip content={"Delete Pod"}>
+                                                <Button icon={<DeleteIcon/>}
+                                                        className="labeled-button"
+                                                        variant="link"
+                                                        onClick={e => {
+                                                            setShowDeleteConfirmation(true);
+                                                            setDeleteEntityType("pod");
+                                                            setDeleteEntityEnv(env);
+                                                            setDeleteEntityName(pod.containerName);
+                                                        }}></Button>
+                                            </Tooltip>
+                                        </Label>
+                                    </Tooltip>
+                                )
+                            }
+                        )}
+                    </LabelGroup>
+                </FlexItem>
+                <FlexItem>{env === "dev" && rolloutButton()}</FlexItem>
+            </Flex>
+        )
+    }
+
+    function getPipelineState(env: string) {
+        const status = pipelineStatuses.filter(p => p.projectId === project.projectId).at(0);
+        const pipeline = status?.pipelineName;
+        const pipelineResult = status?.result;
+        let lastPipelineRunTime = 0;
+        if (status?.startTime) {
+            const start: Date = new Date(status.startTime);
+            const finish: Date = status.completionTime !== undefined && status.completionTime !== null ? new Date(status.completionTime) : new Date();
+            lastPipelineRunTime = Math.round((finish.getTime() - start.getTime()) / 1000);
+        }
+        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 className="not-spinner"/> : <DownIcon className="not-spinner"/>
+        return (
+            <Flex justifyContent={{default: "justifyContentSpaceBetween"}} alignItems={{default: "alignItemsCenter"}}>
+                <FlexItem>
+                    <Tooltip content={pipelineResult} position={"right"}>
+                        <LabelGroup numLabels={2}>
+                            <Label icon={isRunning ? <Spinner diameter="16px" className="spinner"/> : icon}
+                                   color={color}>
+                                {pipeline
+                                    ? <Button className='labeled-button' variant="link" onClick={e =>
+                                        useLogStore.setState({showLog: true, type: 'pipeline', podName: pipeline})
+                                    }>
+                                        {pipeline}
+                                    </Button>
+                                    : "No pipeline"}
+                                {isRunning && <Tooltip content={"Stop PipelineRun"}>
+                                    <Button
+                                        icon={<DeleteIcon/>}
+                                        className="labeled-button"
+                                        variant="link" onClick={e => {
+                                        setShowDeleteConfirmation(true);
+                                        setDeleteEntityType("pipelinerun");
+                                        setDeleteEntityEnv(env);
+                                        setDeleteEntityName(pipeline);
+                                    }}></Button>
+                                </Tooltip>}
+                            </Label>
+                            {pipeline !== undefined && showTime === true && lastPipelineRunTime !== undefined &&
+                                <Label icon={<ClockIcon className="not-spinner"/>}
+                                       color={color}>{lastPipelineRunTime + "s"}</Label>}
+                        </LabelGroup>
+                    </Tooltip>
+                </FlexItem>
+                <FlexItem>{env === "dev" && buildButton(env)}</FlexItem>
+            </Flex>
+        )
+    }
+
+    function getDeleteConfirmation() {
+        return (<Modal
+            className="modal-delete"
+            title="Confirmation"
+            isOpen={showDeleteConfirmation}
+            onClose={() => setShowDeleteConfirmation(false)}
+            actions={[
+                <Button key="confirm" variant="primary" onClick={e => {
+                    if (deleteEntityEnv && deleteEntityName && deleteEntity) {
+                        deleteEntity(deleteEntityType, deleteEntityName, deleteEntityEnv);
+                        setShowDeleteConfirmation(false);
+                    }
+                }}>Delete
+                </Button>,
+                <Button key="cancel" variant="link"
+                        onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
+            ]}
+            onEscapePress={e => setShowDeleteConfirmation(false)}>
+            <div>{"Delete " + deleteEntity + " " + deleteEntityName + "?"}</div>
+        </Modal>)
+    }
+
+    const env = props.env;
+    return (
+        <Card className="project-status">
+            <CardBody>
+                <DescriptionList isHorizontal>
+                    <DescriptionListGroup>
+                        <DescriptionListTerm>Environment</DescriptionListTerm>
+                        <DescriptionListDescription>
+                            <Badge className="badge">{env}</Badge>
+                        </DescriptionListDescription>
+                    </DescriptionListGroup>
+                    <DescriptionListGroup>
+                        <DescriptionListTerm>Pipeline</DescriptionListTerm>
+                        <DescriptionListDescription>
+                            {getPipelineState(env)}
+                        </DescriptionListDescription>
+                    </DescriptionListGroup>
+                    <DescriptionListGroup>
+                        <DescriptionListTerm>Deployment</DescriptionListTerm>
+                        <DescriptionListDescription>
+                            {getReplicasPanel(env)}
+                        </DescriptionListDescription>
+                    </DescriptionListGroup>
+                    <DescriptionListGroup>
+                        <DescriptionListTerm>Pods</DescriptionListTerm>
+                        <DescriptionListDescription>
+                            {getPodsPanel(env)}
+                        </DescriptionListDescription>
+                    </DescriptionListGroup>
+                </DescriptionList>
+            </CardBody>
+            {showDeleteConfirmation && getDeleteConfirmation()}
+        </Card>
+    )
+}
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/build/ProjectBuildTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/build/ProjectBuildTab.tsx
new file mode 100644
index 00000000..89d4bbbf
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/webui/src/project/build/ProjectBuildTab.tsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import '../../designer/karavan.css';
+import {BuildStatus} from "./BuildStatus";
+import {PageSection} from "@patternfly/react-core";
+import {useAppConfigStore, useProjectStore} from "../../api/ProjectStore";
+
+
+export function ProjectBuildTab () {
+
+    const {config} = useAppConfigStore();
+    const {project} = useProjectStore();
+
+    return (
+        <PageSection className="project-tab-panel" padding={{default: "padding"}}>
+            <div className="project-operations">
+                {/*{["dev", "test", "prod"].map(env =>*/}
+                {config.environments.map(env =>
+                    <BuildStatus env={env}/>
+                )}
+            </div>
+        </PageSection>
+    )
+}