diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodDescribe.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodDescribe.tsx new file mode 100644 index 000000000..ed91a6e2f --- /dev/null +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodDescribe.tsx @@ -0,0 +1,454 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { useEffect, useState } from "react"; +import { connect } from "react-redux"; +import { Theme } from "@mui/material/styles"; +import createStyles from "@mui/styles/createStyles"; +import withStyles from "@mui/styles/withStyles"; +import { + actionsTray, + buttonsStyles, + hrClass, + searchField, +} from "../../../Common/FormComponents/common/styleLibrary"; +import { Box } from "@mui/material"; +import Grid from "@mui/material/Grid"; +import Chip from "@mui/material/Chip"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Tabs from "@mui/material/Tabs"; +import Tab from "@mui/material/Tab"; +import TableContainer from "@mui/material/TableContainer"; +import Paper from "@mui/material/Paper"; +import { setErrorSnackMessage } from "../../../../../actions"; +import { ErrorResponseHandler } from "../../../../../common/types"; +import api from "../../../../../common/api"; +import { AppState } from "../../../../../store"; +import LabelValuePair from "../../../Common/UsageBarWrapper/LabelValuePair"; + +interface IPodEventsProps { + classes: any; + tenant: string; + namespace: string; + podName: string; + propLoading: boolean; + setErrorSnackMessage: typeof setErrorSnackMessage; + loadingTenant: boolean; +} + +interface Annotation { + key: string; + value: string; +} + +interface Condition { + status: string; + type: string; +} + +interface EnvVar { + key: string; + value: string; +} + +interface Mount { + mountPath: string; + name: string; +} + +interface State { + started: string; + state: string; +} + +interface Container { + args: string[]; + containerID: string; + environmentVariables: EnvVar[]; + hostPorts: string[]; + image: string; + imageID: string; + lastState: any; + mounts: Mount[]; + name: string; + ports: string[]; + ready: boolean; + state: State +} + +interface Label { + key: string; + value: string; +} + +interface Toleration { + effect: string; + key: string; + operator: string; + tolerationSeconds: number; +} + +interface VolumePVC{ + claimName: string; +} + +interface Volume { + name: string; + pvc?: VolumePVC; + projected?: any; +} + +interface DescribeResponse { + annotations: Annotation[]; + conditions: Condition[]; + containers: Container[]; + controllerRef: string; + labels: Label[]; + name: string; + namespace: string; + nodeName: string; + nodeSelector: string[]; + phase: string; + podIP: string; + qosClass: string; + startTime: string; + tolerations: Toleration[]; + volumes: Volume[]; +} + +interface IPodDescribeSummaryProps { + describeInfo: DescribeResponse; +} + +interface IPodDescribeAnnotationsProps { + annotations: Annotation[]; +} + +interface IPodDescribeLabelsProps { + labels: Label[]; +} + +interface IPodDescribeConditionsProps { + conditions: Condition[]; +} + +interface IPodDescribeTolerationsProps { + tolerations: Toleration[]; +} + +interface IPodDescribeVolumesProps { + volumes: Volume[]; +} + +interface IPodDescribeContainersProps { + containers: Container[]; +} + +interface IPodDescribeTableProps { + title: string; + columns: string[]; + columnsLabels: string[]; + items: any[]; +} + + +const styles = (theme: Theme) => + createStyles({ + ...actionsTray, + ...buttonsStyles, + ...searchField, + ...hrClass, + actionsTray: { + ...actionsTray.actionsTray, + padding: "15px 0 0", + }, + }); + +const twoColCssGridLayoutConfig = { + display: "grid", + gridTemplateColumns: { xs: "1fr", sm: "2fr 1fr" }, + gridAutoFlow: { xs: "dense", sm: "row" }, + gap: 2, + padding: "15px", +}; + +const HeaderSection = ({ title }: { title: string }) => { + return ( + +

{title}

+
+ + ); +}; + +const PodDescribeSummary = ({describeInfo}: IPodDescribeSummaryProps) => { + return ( + + + + + + + + + + + + + ); +}; + +const PodDescribeAnnotations = ({annotations}: IPodDescribeAnnotationsProps) => { + return ( + + + + {annotations.map((annotation, index) => ( + + ))} + + + ); +}; + +const PodDescribeLabels = ({labels}: IPodDescribeLabelsProps) => { + return ( + + + + {labels.map((label, index) => ( + + ))} + + + ); +}; + + +const PodDescribeConditions = ({conditions}: IPodDescribeConditionsProps) => { + return ; +}; + +const PodDescribeTolerations = ({tolerations}: IPodDescribeTolerationsProps) => { + return ; +}; + +const PodDescribeVolumes = ({volumes}: IPodDescribeVolumesProps) => { + return ( + + {volumes.map((volume, index) => ( + + + + {volume.pvc && ( + + + + + )} + {/* TODO Add component to display projected data (Maybe change API response) */} + {volume.projected && } + + + ))} + + ); +}; + +const PodDescribeTable = ({title, items, columns, columnsLabels}: IPodDescribeTableProps) => { + return ( + + + + + + + + {columnsLabels.map((label, index) => {label} )} + + + + {items.map((item, i) => ( + + {columns.map((column, j) => {item[column]} )} + + ))} + +
+
+
+
+ ); +}; + + +const PodDescribeContainers = ({containers}: IPodDescribeContainersProps) => { + return ( + + {containers.map((container, index) => ( + + + + + + + + + + + + + + + + + + + + ))} + + ); +}; + + +const PodDescribe = ({ + classes, + tenant, + namespace, + podName, + propLoading, + setErrorSnackMessage, + loadingTenant, +}: IPodEventsProps) => { + const [describeInfo, setDescribeInfo] = useState(); + const [loading, setLoading] = useState(true); + const [curTab, setCurTab] = useState(0); + + useEffect(() => { + if (propLoading) { + setLoading(true); + } + }, [propLoading]); + + useEffect(() => { + if (loadingTenant) { + setLoading(true); + } + }, [loadingTenant]); + + useEffect(() => { + if (loading) { + api + .invoke( + "GET", + `/api/v1/namespaces/${namespace}/tenants/${tenant}/pods/${podName}/describe` + ) + .then((res: DescribeResponse) => { + setDescribeInfo(res); + setLoading(false); + }) + .catch((err: ErrorResponseHandler) => { + setErrorSnackMessage(err); + setLoading(false); + }); + } + }, [loading, podName, namespace, tenant, setErrorSnackMessage]); + + const renderTabComponent = (index: number, info: DescribeResponse) => { + switch (index) { + case 0: + return + case 1: + return + case 2: + return + case 3: + return + case 4: + return + case 5: + return + case 6: + return + default: + break; + } + }; + return ( + + {describeInfo && ( + , newValue: number) => { + setCurTab(newValue); + }} + indicatorColor="primary" + textColor="primary" + aria-label="cluster-tabs" + variant="scrollable" + scrollButtons="auto"> + + + + + + + + + {renderTabComponent(curTab, describeInfo)} + )} + + ); +}; +const mapState = (state: AppState) => ({ + loadingTenant: state.tenants.tenantDetails.loadingTenant, +}); +const connector = connect(mapState, { + setErrorSnackMessage, +}); + +export default withStyles(styles)(connector(PodDescribe)); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodDetails.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodDetails.tsx index db5c59927..1b736aad7 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodDetails.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodDetails.tsx @@ -26,6 +26,7 @@ import { Link } from "react-router-dom"; import { setErrorSnackMessage } from "../../../../../actions"; import PodLogs from "./PodLogs"; import PodEvents from "./PodEvents"; +import PodDescribe from "./PodDescribe"; interface IPodDetailsProps { classes: any; @@ -89,7 +90,8 @@ const PodDetails = ({ classes, match }: IPodDetailsProps) => { scrollButtons="auto" > - + + {curTab === 0 && ( @@ -101,6 +103,14 @@ const PodDetails = ({ classes, match }: IPodDetailsProps) => { /> )} {curTab === 1 && ( + + )} + {curTab === 2 && (