diff --git a/cyclops-ctrl/internal/handler/handler.go b/cyclops-ctrl/internal/handler/handler.go index 862d15aa..5ac2246e 100644 --- a/cyclops-ctrl/internal/handler/handler.go +++ b/cyclops-ctrl/internal/handler/handler.go @@ -79,6 +79,8 @@ func (h *Handler) Start() error { h.router.GET("/resources/pods/:namespace/:name/:container/logs", modulesController.GetLogs) h.router.GET("/resources/pods/:namespace/:name/:container/logs/download", modulesController.DownloadLogs) + h.router.GET("/resources/deployments/:namespace/:deployment/:container/logs", modulesController.GetDeploymentLogs) + h.router.GET("/resources/statefulsets/:namespace/:name/:container/logs", modulesController.GetStatefulSetsLogs) h.router.GET("/manifest", modulesController.GetManifest) h.router.GET("/resources", modulesController.GetResource) diff --git a/cyclops-ui/src/components/k8s-resources/Deployment.tsx b/cyclops-ui/src/components/k8s-resources/Deployment.tsx index 182f357e..e68c0d15 100644 --- a/cyclops-ui/src/components/k8s-resources/Deployment.tsx +++ b/cyclops-ui/src/components/k8s-resources/Deployment.tsx @@ -22,8 +22,21 @@ interface Props { namespace: string; } +interface container { + name: string; +} +interface pod { + name: string; + containers: container[]; +} + +interface deployment { + status: string; + pods: pod[]; +} + const Deployment = ({ name, namespace }: Props) => { - const [deployment, setDeployment] = useState({ + const [deployment, setDeployment] = useState({ status: "", pods: [], }); @@ -35,6 +48,10 @@ const Deployment = ({ name, namespace }: Props) => { containers: [], initContainers: [], }); + const [deploymentLogs, setDeploymentLogs] = useState(""); + const [deploymentLogsModal, setDeploymentLogsModal] = useState({ + on: false, + }); const [error, setError] = useState({ message: "", description: "", @@ -78,6 +95,13 @@ const Deployment = ({ name, namespace }: Props) => { setLogs(""); }; + const handleCancelDeploymentLogs = () => { + setDeploymentLogsModal({ + on: false, + }); + setDeploymentLogs(""); + }; + const downloadLogs = (container: string) => { return function () { window.location.href = @@ -181,134 +205,220 @@ const Deployment = ({ name, namespace }: Props) => { }); }; - return ( -
- {error.message.length !== 0 && ( - { - setError({ - message: "", - description: "", + const getDeploymentLogsTabItems = () => { + let items: TabsProps["items"] = []; + let keys: string[] = []; + + if (deployment.pods !== null) { + for (var pod of deployment.pods) { + for (var container of pod.containers) { + if (!keys.includes(container.name)) { + items.push({ + key: `${container.name}`, + label: `${container.name}`, + children: ( + + + + ), }); - }} - style={{ marginBottom: "20px" }} - /> - )} - - - Replicas: {deployment.pods.length} - - - - - - - {formatPodAge(value)}} - /> - ( - <> - {containers.map((container: any) => { - let color = container.status.running ? "green" : "red"; + keys.push(container.name); + } + } + } + } - if (record.podPhase === "Pending") { - color = "yellow"; - } + return items; + }; - return ( - - {container.image} - - ); - })} - - )} - /> - ( - <> - - - )} - /> -
- -
- { + axios + .get( + "/api/resources/deployments/" + + namespace + + "/" + + name + + "/" + + container + + "/logs", + ) + .then((res) => { + if (res.data) { + let log = ""; + res.data.forEach((s: string) => { + log += s; + log += "\n"; + }); + setDeploymentLogs(log); + } else { + setDeploymentLogs("No logs available"); + } + }) + .catch((error) => { + setError(mapResponseError(error)); + }); + }; + + return ( + <> +
+ View Logs + +
+ {error.message.length !== 0 && ( + { + setError({ + message: "", + description: "", + }); + }} + style={{ marginBottom: "20px" }} + /> + )} + + + + Replicas: {deployment.pods.length} + + + + + + + {formatPodAge(value)}} + /> + ( + <> + {containers.map((container: any) => { + let color = container.status.running ? "green" : "red"; + + if (record.podPhase === "Pending") { + color = "yellow"; + } + + return ( + + {container.image} + + ); + })} + + )} + /> + ( + <> + + + )} + /> +
+ +
+ + + + + + +
+ ); }; diff --git a/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx b/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx index da19ddea..d1906a55 100644 --- a/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx +++ b/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx @@ -22,8 +22,21 @@ interface Props { namespace: string; } +interface container { + name: string; +} +interface pod { + name: string; + containers: container[]; +} + +interface statefulSet { + status: string; + pods: pod[]; +} + const StatefulSet = ({ name, namespace }: Props) => { - const [statefulSet, setStatefulSet] = useState({ + const [statefulSet, setStatefulSet] = useState({ status: "", pods: [], }); @@ -35,6 +48,10 @@ const StatefulSet = ({ name, namespace }: Props) => { containers: [], initContainers: [], }); + const [statefulSetLogs, setStatefulSetLogs] = useState(""); + const [statefulSetLogsModal, setStatefulSetLogsModal] = useState({ + on: false, + }); const [error, setError] = useState({ message: "", description: "", @@ -78,6 +95,13 @@ const StatefulSet = ({ name, namespace }: Props) => { setLogs(""); }; + const handleCancelStatefulSetLogs = () => { + setStatefulSetLogsModal({ + on: false, + }); + setStatefulSetLogs(""); + }; + const downloadLogs = (container: string) => { return function () { window.location.href = @@ -181,134 +205,219 @@ const StatefulSet = ({ name, namespace }: Props) => { }); }; - return ( -
- {error.message.length !== 0 && ( - { - setError({ - message: "", - description: "", + const getStatefulSetLogsTabItems = () => { + let items: TabsProps["items"] = []; + let keys: string[] = []; + + if (statefulSet.pods !== null) { + for (var pod of statefulSet.pods) { + for (var container of pod.containers) { + if (!keys.includes(container.name)) { + items.push({ + key: `${container.name}`, + label: `${container.name}`, + children: ( + + + + ), }); - }} - style={{ marginBottom: "20px" }} - /> - )} - - - Replicas: {statefulSet.pods.length} - - - - - - - {formatPodAge(value)}} - /> - ( - <> - {containers.map((container: any, record: any) => { - let color = container.status.running ? "green" : "red"; + keys.push(container.name); + } + } + } + } - if (record.podPhase === "Pending") { - color = "yellow"; - } + return items; + }; - return ( - - {container.image} - - ); - })} - - )} - /> - ( - <> - - - )} - /> -
- -
- { + axios + .get( + "/api/resources/statefulsets/" + + namespace + + "/" + + name + + "/" + + container + + "/logs", + ) + .then((res) => { + if (res.data) { + let log = ""; + res.data.forEach((s: string) => { + log += s; + log += "\n"; + }); + setStatefulSetLogs(log); + } else { + setStatefulSetLogs("No logs available"); + } + }) + .catch((error) => { + setError(mapResponseError(error)); + }); + }; + + return ( + <> +
+ View Logs + +
+ {error.message.length !== 0 && ( + { + setError({ + message: "", + description: "", + }); + }} + style={{ marginBottom: "20px" }} + /> + )} + + + Replicas: {statefulSet.pods.length} + + + + + + + {formatPodAge(value)}} + /> + ( + <> + {containers.map((container: any, record: any) => { + let color = container.status.running ? "green" : "red"; + + if (record.podPhase === "Pending") { + color = "yellow"; + } + + return ( + + {container.image} + + ); + })} + + )} + /> + ( + <> + + + )} + /> +
+ +
+ + + + + + +
+ ); }; diff --git a/cyclops-ui/src/components/pages/ModuleDetails.tsx b/cyclops-ui/src/components/pages/ModuleDetails.tsx index f95a0b32..cd2f31f7 100644 --- a/cyclops-ui/src/components/pages/ModuleDetails.tsx +++ b/cyclops-ui/src/components/pages/ModuleDetails.tsx @@ -520,24 +520,19 @@ const ModuleDetails = () => { {resource.namespace} - - - - - + {resourceDetails} , );