From bde5ad2a223000162336aaa903d02af3c40941c1 Mon Sep 17 00:00:00 2001 From: Sheikh-Abubaker Date: Mon, 28 Oct 2024 17:44:45 +0530 Subject: [PATCH 1/6] Add button to fetch Deployment/StatefulSet logs Signed-off-by: Sheikh-Abubaker --- .../components/k8s-resources/Deployment.tsx | 274 ++++++++++++++++- .../ResourceList/ResourceList.tsx | 22 +- .../components/k8s-resources/StatefulSet.tsx | 277 +++++++++++++++++- 3 files changed, 561 insertions(+), 12 deletions(-) diff --git a/cyclops-ui/src/components/k8s-resources/Deployment.tsx b/cyclops-ui/src/components/k8s-resources/Deployment.tsx index e94cc92e..d87a2fd4 100644 --- a/cyclops-ui/src/components/k8s-resources/Deployment.tsx +++ b/cyclops-ui/src/components/k8s-resources/Deployment.tsx @@ -1,9 +1,12 @@ -import React, { useCallback, useEffect, useState } from "react"; -import { Col, Divider, Row, Alert } from "antd"; +import React, { useCallback, useEffect, useState, useRef } from "react"; +import { Col, Divider, Row, Alert, TabsProps, Button, Tabs, Modal } from "antd"; import axios from "axios"; import { mapResponseError } from "../../utils/api/errors"; import PodTable from "./common/PodTable/PodTable"; import { isStreamingEnabled } from "../../utils/api/common"; +import { logStream } from "../../utils/api/sse/logs"; +import ReactAce from "react-ace/lib/ace"; +import { ReadOutlined } from "@ant-design/icons"; interface Props { name: string; @@ -11,7 +14,7 @@ interface Props { workload: any; } -const Deployment = ({ name, namespace, workload }: Props) => { +export const Deployment = ({ name, namespace, workload }: Props) => { const [deployment, setDeployment] = useState({ status: "", pods: [], @@ -108,4 +111,267 @@ const Deployment = ({ name, namespace, workload }: Props) => { ); }; -export default Deployment; +export const DeploymentLogsButton = ({ name, namespace, workload }: Props) => { + const [logs, setLogs] = useState([]); + const [logsModal, setLogsModal] = useState({ + on: false, + containers: [], + initContainers: [], + }); + + const logsSignalControllerRef = useRef(null); + + const [error, setError] = useState({ + message: "", + description: "", + }); + + const handleCancelLogs = () => { + setLogsModal({ + on: false, + containers: [], + initContainers: [], + }); + setLogs([]); + + // send the abort signal + if (logsSignalControllerRef.current !== null) { + logsSignalControllerRef.current.abort(); + } + }; + + const getTabItems = () => { + let items: TabsProps["items"] = []; + + let container: any; + + if (logsModal.containers !== null) { + for (container of logsModal.containers) { + items.push({ + key: container.name, + label: container.name, + children: ( + + + + + + ), + }); + } + } + + if (logsModal.initContainers !== null) { + for (container of logsModal.initContainers) { + items.push({ + key: container.name, + label: "(init container) " + container.name, + children: ( + + + + + + ), + }); + } + } + + return items; + }; + + const onLogsTabsChange = (container: string) => { + const controller = new AbortController(); + if (logsSignalControllerRef.current !== null) { + logsSignalControllerRef.current.abort(); + } + logsSignalControllerRef.current = controller; // store the controller to be able to abort the request + setLogs(() => []); + + if (isStreamingEnabled()) { + logStream( + name, + namespace, + workload.pods[0].containers[0].name, + (log, isReset = false) => { + if (isReset) { + setLogs(() => []); + } else { + setLogs((prevLogs) => { + return [...prevLogs, log]; + }); + } + }, + (err, isReset = false) => { + if (isReset) { + setError({ + message: "", + description: "", + }); + } else { + setError(mapResponseError(err)); + } + }, + controller, + ); + } else { + axios + .get( + "/api/resources/deployments/" + + namespace + + "/" + + name + + "/" + + workload.pods[0].containers[0].name + + "/logs", + ) + .then((res) => { + if (res.data) { + setLogs(res.data); + } else { + setLogs(() => []); + } + }) + .catch((error) => { + setError(mapResponseError(error)); + }); + } + }; + + const downloadLogs = (container: string) => { + return function () { + window.location.href = + "/api/resources/pods/" + + namespace + + "/" + + workload.pods[0].name + + "/" + + container + + "/logs/download"; + }; + }; + + return ( + <> + + + {error.message.length !== 0 && ( + { + setError({ + message: "", + description: "", + }); + }} + style={{ paddingBottom: "20px" }} + /> + )} + + + + ); +}; diff --git a/cyclops-ui/src/components/k8s-resources/ResourceList/ResourceList.tsx b/cyclops-ui/src/components/k8s-resources/ResourceList/ResourceList.tsx index 15ee2328..41a6b0bc 100644 --- a/cyclops-ui/src/components/k8s-resources/ResourceList/ResourceList.tsx +++ b/cyclops-ui/src/components/k8s-resources/ResourceList/ResourceList.tsx @@ -20,11 +20,11 @@ import { ResourceRef, resourceRefKey, } from "../../../utils/resourceRef"; -import Deployment from "../Deployment"; +import { Deployment, DeploymentLogsButton } from "../Deployment"; import CronJob from "../CronJob"; import Job from "../Job"; import DaemonSet from "../DaemonSet"; -import StatefulSet from "../StatefulSet"; +import { StatefulSet, StatefulSetLogsButton } from "../StatefulSet"; import Pod from "../Pod"; import Service from "../Service"; import ConfigMap from "../ConfigMap"; @@ -560,6 +560,24 @@ const ResourceList = ({ /> )} + {resource.kind === "Deployment" && ( + + + + )} + {resource.kind === "StatefulSet" && ( + + + + )} {resourceDetails} , diff --git a/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx b/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx index a967cfc9..81180126 100644 --- a/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx +++ b/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx @@ -1,9 +1,12 @@ -import React, { useCallback, useEffect, useState } from "react"; -import { Col, Divider, Row, Alert } from "antd"; +import React, { useCallback, useEffect, useRef, useState } from "react"; +import { Col, Divider, Row, Alert, TabsProps, Button, Modal, Tabs } from "antd"; import axios from "axios"; import { mapResponseError } from "../../utils/api/errors"; import PodTable from "./common/PodTable/PodTable"; import { isStreamingEnabled } from "../../utils/api/common"; +import ReactAce from "react-ace/lib/ace"; +import { logStream } from "../../utils/api/sse/logs"; +import { ReadOutlined } from "@ant-design/icons"; interface Props { name: string; @@ -11,12 +14,11 @@ interface Props { workload: any; } -const StatefulSet = ({ name, namespace, workload }: Props) => { +export const StatefulSet = ({ name, namespace, workload }: Props) => { const [statefulSet, setStatefulSet] = useState({ status: "", pods: [], }); - const [error, setError] = useState({ message: "", description: "", @@ -86,7 +88,7 @@ const StatefulSet = ({ name, namespace, workload }: Props) => { description: "", }); }} - style={{ marginBottom: "20px" }} + style={{ paddingBottom: "20px" }} /> )} @@ -109,4 +111,267 @@ const StatefulSet = ({ name, namespace, workload }: Props) => { ); }; -export default StatefulSet; +export const StatefulSetLogsButton = ({ name, namespace, workload }: Props) => { + const [logs, setLogs] = useState([]); + const [logsModal, setLogsModal] = useState({ + on: false, + containers: [], + initContainers: [], + }); + + const logsSignalControllerRef = useRef(null); + + const [error, setError] = useState({ + message: "", + description: "", + }); + + const handleCancelLogs = () => { + setLogsModal({ + on: false, + containers: [], + initContainers: [], + }); + setLogs([]); + + // send the abort signal + if (logsSignalControllerRef.current !== null) { + logsSignalControllerRef.current.abort(); + } + }; + + const getTabItems = () => { + let items: TabsProps["items"] = []; + + let container: any; + + if (logsModal.containers !== null) { + for (container of logsModal.containers) { + items.push({ + key: container.name, + label: container.name, + children: ( + + + + + + ), + }); + } + } + + if (logsModal.initContainers !== null) { + for (container of logsModal.initContainers) { + items.push({ + key: container.name, + label: "(init container) " + container.name, + children: ( + + + + + + ), + }); + } + } + + return items; + }; + + const onLogsTabsChange = (container: string) => { + const controller = new AbortController(); + if (logsSignalControllerRef.current !== null) { + logsSignalControllerRef.current.abort(); + } + logsSignalControllerRef.current = controller; // store the controller to be able to abort the request + setLogs(() => []); + + if (isStreamingEnabled()) { + logStream( + name, + namespace, + workload.pods[0].containers[0].name, + (log, isReset = false) => { + if (isReset) { + setLogs(() => []); + } else { + setLogs((prevLogs) => { + return [...prevLogs, log]; + }); + } + }, + (err, isReset = false) => { + if (isReset) { + setError({ + message: "", + description: "", + }); + } else { + setError(mapResponseError(err)); + } + }, + controller, + ); + } else { + axios + .get( + "/api/resources/deployments/" + + namespace + + "/" + + name + + "/" + + workload.pods[0].containers[0].name + + "/logs", + ) + .then((res) => { + if (res.data) { + setLogs(res.data); + } else { + setLogs(() => []); + } + }) + .catch((error) => { + setError(mapResponseError(error)); + }); + } + }; + + const downloadLogs = (container: string) => { + return function () { + window.location.href = + "/api/resources/pods/" + + namespace + + "/" + + workload.pods[0].name + + "/" + + container + + "/logs/download"; + }; + }; + + return ( + <> + + + {error.message.length !== 0 && ( + { + setError({ + message: "", + description: "", + }); + }} + style={{ paddingBottom: "20px" }} + /> + )} + + + + ); +}; From 0e9b4911971cb33d635537bf60a2473f80176b42 Mon Sep 17 00:00:00 2001 From: Sheikh-Abubaker Date: Mon, 28 Oct 2024 21:47:47 +0530 Subject: [PATCH 2/6] Revert style to marginBottom and remove container parameter from onLogsTabsChange Signed-off-by: Sheikh-Abubaker --- cyclops-ui/src/components/k8s-resources/Deployment.tsx | 4 ++-- cyclops-ui/src/components/k8s-resources/StatefulSet.tsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cyclops-ui/src/components/k8s-resources/Deployment.tsx b/cyclops-ui/src/components/k8s-resources/Deployment.tsx index d87a2fd4..37f1adc1 100644 --- a/cyclops-ui/src/components/k8s-resources/Deployment.tsx +++ b/cyclops-ui/src/components/k8s-resources/Deployment.tsx @@ -208,7 +208,7 @@ export const DeploymentLogsButton = ({ name, namespace, workload }: Props) => { return items; }; - const onLogsTabsChange = (container: string) => { + const onLogsTabsChange = () => { const controller = new AbortController(); if (logsSignalControllerRef.current !== null) { logsSignalControllerRef.current.abort(); @@ -367,7 +367,7 @@ export const DeploymentLogsButton = ({ name, namespace, workload }: Props) => { description: "", }); }} - style={{ paddingBottom: "20px" }} + style={{ marginBottom: "20px" }} /> )} diff --git a/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx b/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx index 81180126..4174e4f4 100644 --- a/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx +++ b/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx @@ -88,7 +88,7 @@ export const StatefulSet = ({ name, namespace, workload }: Props) => { description: "", }); }} - style={{ paddingBottom: "20px" }} + style={{ marginBottom: "20px" }} /> )} @@ -208,7 +208,7 @@ export const StatefulSetLogsButton = ({ name, namespace, workload }: Props) => { return items; }; - const onLogsTabsChange = (container: string) => { + const onLogsTabsChange = () => { const controller = new AbortController(); if (logsSignalControllerRef.current !== null) { logsSignalControllerRef.current.abort(); @@ -367,7 +367,7 @@ export const StatefulSetLogsButton = ({ name, namespace, workload }: Props) => { description: "", }); }} - style={{ paddingBottom: "20px" }} + style={{ marginBottom: "20px" }} /> )} From 47a9c933dd41edd5527326afb6b4eed4197d7df6 Mon Sep 17 00:00:00 2001 From: Sheikh-Abubaker Date: Tue, 29 Oct 2024 02:32:01 +0530 Subject: [PATCH 3/6] Add handler functions for Deployment/Sts logs and point axios.get to correct sts api Signed-off-by: Sheikh-Abubaker --- cyclops-ctrl/internal/handler/handler.go | 5 ++++- cyclops-ui/src/components/k8s-resources/StatefulSet.tsx | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cyclops-ctrl/internal/handler/handler.go b/cyclops-ctrl/internal/handler/handler.go index 08e5019d..e4c1f32c 100644 --- a/cyclops-ctrl/internal/handler/handler.go +++ b/cyclops-ctrl/internal/handler/handler.go @@ -1,9 +1,10 @@ package handler import ( + "net/http" + "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/controller/sse" "github.com/gin-gonic/gin" - "net/http" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/controller" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/prometheus" @@ -86,6 +87,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/stream", sse.HeadersMiddleware(), modulesController.GetLogsStream) 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/StatefulSet.tsx b/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx index 4174e4f4..8bd5e494 100644 --- a/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx +++ b/cyclops-ui/src/components/k8s-resources/StatefulSet.tsx @@ -245,7 +245,7 @@ export const StatefulSetLogsButton = ({ name, namespace, workload }: Props) => { } else { axios .get( - "/api/resources/deployments/" + + "/api/resources/statefulsets/" + namespace + "/" + name + @@ -316,7 +316,7 @@ export const StatefulSetLogsButton = ({ name, namespace, workload }: Props) => { } else { axios .get( - "/api/resources/deployments/" + + "/api/resources/statefulsets/" + namespace + "/" + name + From 0b19391de6349e22b77b0a99fa53a22d5fd21a35 Mon Sep 17 00:00:00 2001 From: Sheikh-Abubaker Date: Sun, 15 Dec 2024 01:03:40 +0530 Subject: [PATCH 4/6] Resolve Conflicts Signed-off-by: Sheikh-Abubaker --- .../components/k8s-resources/Deployment.tsx | 110 ++++++++---------- .../components/k8s-resources/StatefulSet.tsx | 108 ++++++++--------- 2 files changed, 101 insertions(+), 117 deletions(-) diff --git a/cyclops-ui/src/components/k8s-resources/Deployment.tsx b/cyclops-ui/src/components/k8s-resources/Deployment.tsx index 18ef34fc..93744e34 100644 --- a/cyclops-ui/src/components/k8s-resources/Deployment.tsx +++ b/cyclops-ui/src/components/k8s-resources/Deployment.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState, useRef } from "react"; +import { useCallback, useEffect, useState, useRef } from "react"; import { Col, Divider, @@ -10,13 +10,12 @@ import { Tabs, Modal, } from "antd"; -import axios from "axios"; import { mapResponseError } from "../../utils/api/errors"; import PodTable from "./common/PodTable/PodTable"; import { isStreamingEnabled } from "../../utils/api/common"; import { logStream } from "../../utils/api/sse/logs"; import ReactAce from "react-ace/lib/ace"; -import { ReadOutlined } from "@ant-design/icons"; +import { DownloadOutlined, ReadOutlined } from "@ant-design/icons"; import { useResourceListActions } from "./ResourceList/ResourceListActionsContext"; interface Props { @@ -122,6 +121,8 @@ export const Deployment = ({ name, namespace, workload }: Props) => { }; export const DeploymentLogsButton = ({ name, namespace, workload }: Props) => { + const { streamingDisabled, getPodLogs, downloadPodLogs, streamPodLogs } = + useResourceListActions(); const [logs, setLogs] = useState([]); const [logsModal, setLogsModal] = useState({ on: false, @@ -162,15 +163,23 @@ export const DeploymentLogsButton = ({ name, namespace, workload }: Props) => { label: container.name, children: ( - - + {downloadPodLogs ? ( +
+ + +
+ ) : ( + <> + )} { label: "(init container) " + container.name, children: ( - - + {downloadPodLogs ? ( +
+ + +
+ ) : ( + <> + )} { logsSignalControllerRef.current = controller; // store the controller to be able to abort the request setLogs(() => []); - if (isStreamingEnabled()) { + if (!streamingDisabled) { logStream( - name, namespace, + name, workload.pods[0].containers[0].name, (log, isReset = false) => { if (isReset) { @@ -251,21 +268,13 @@ export const DeploymentLogsButton = ({ name, namespace, workload }: Props) => { } }, controller, + streamPodLogs, ); } else { - axios - .get( - "/api/resources/deployments/" + - namespace + - "/" + - name + - "/" + - workload.pods[0].containers[0].name + - "/logs", - ) + getPodLogs(namespace, name, workload.pods[0].containers[0].name) .then((res) => { - if (res.data) { - setLogs(res.data); + if (res) { + setLogs(res); } else { setLogs(() => []); } @@ -277,16 +286,7 @@ export const DeploymentLogsButton = ({ name, namespace, workload }: Props) => { }; const downloadLogs = (container: string) => { - return function () { - window.location.href = - "/api/resources/pods/" + - namespace + - "/" + - workload.pods[0].name + - "/" + - container + - "/logs/download"; - }; + return () => downloadPodLogs(namespace, workload.pods[0].name, container); }; return ( @@ -294,13 +294,13 @@ export const DeploymentLogsButton = ({ name, namespace, workload }: Props) => { - + {downloadPodLogs ? ( +
+ + +
+ ) : ( + <> + )} { label: "(init container) " + container.name, children: ( - - + {downloadPodLogs ? ( +
+ + +
+ ) : ( + <> + )} { logsSignalControllerRef.current = controller; // store the controller to be able to abort the request setLogs(() => []); - if (isStreamingEnabled()) { + if (!streamingDisabled) { logStream( - name, namespace, + name, workload.pods[0].containers[0].name, (log, isReset = false) => { if (isReset) { @@ -250,21 +268,13 @@ export const StatefulSetLogsButton = ({ name, namespace, workload }: Props) => { } }, controller, + streamPodLogs, ); } else { - axios - .get( - "/api/resources/statefulsets/" + - namespace + - "/" + - name + - "/" + - workload.pods[0].containers[0].name + - "/logs", - ) + getPodLogs(namespace, name, workload.pods[0].containers[0].name) .then((res) => { - if (res.data) { - setLogs(res.data); + if (res) { + setLogs(res); } else { setLogs(() => []); } @@ -276,16 +286,7 @@ export const StatefulSetLogsButton = ({ name, namespace, workload }: Props) => { }; const downloadLogs = (container: string) => { - return function () { - window.location.href = - "/api/resources/pods/" + - namespace + - "/" + - workload.pods[0].name + - "/" + - container + - "/logs/download"; - }; + return () => downloadPodLogs(namespace, workload.pods[0].name, container); }; return ( @@ -298,8 +299,8 @@ export const StatefulSetLogsButton = ({ name, namespace, workload }: Props) => { logsSignalControllerRef.current = controller; // store the controller to be able to abort the request logStream( - workload.pods[0].name, namespace, + workload.pods[0].name, workload.pods[0].containers[0].name, (log, isReset = false) => { if (isReset) { @@ -323,19 +324,10 @@ export const StatefulSetLogsButton = ({ name, namespace, workload }: Props) => { controller, ); } else { - axios - .get( - "/api/resources/statefulsets/" + - namespace + - "/" + - name + - "/" + - workload.pods[0].containers[0].name + - "/logs", - ) + getPodLogs(namespace, name, workload.pods[0].containers[0].name) .then((res) => { - if (res.data) { - setLogs(res.data); + if (res) { + setLogs(res); } else { setLogs(() => []); } From d2c94aee0c8bb46ba3cbf6de6b29ec706ab6b578 Mon Sep 17 00:00:00 2001 From: Sheikh-Abubaker Date: Mon, 10 Feb 2025 01:17:25 +0530 Subject: [PATCH 5/6] Introduce a separated reusable component for object logs Signed-off-by: Sheikh-Abubaker --- cyclops-ctrl/internal/handler/handler.go | 4 +- .../components/k8s-resources/Deployment.tsx | 263 +---------------- .../ResourceList/ResourceList.tsx | 19 +- .../components/k8s-resources/StatefulSet.tsx | 276 +----------------- .../k8s-resources/common/ObjectLogsButton.tsx | 274 +++++++++++++++++ 5 files changed, 288 insertions(+), 548 deletions(-) create mode 100644 cyclops-ui/src/components/k8s-resources/common/ObjectLogsButton.tsx diff --git a/cyclops-ctrl/internal/handler/handler.go b/cyclops-ctrl/internal/handler/handler.go index accfa4d2..6e0c271a 100644 --- a/cyclops-ctrl/internal/handler/handler.go +++ b/cyclops-ctrl/internal/handler/handler.go @@ -97,8 +97,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/stream", sse.HeadersMiddleware(), modulesController.GetLogsStream) 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("/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 93744e34..7a0823c0 100644 --- a/cyclops-ui/src/components/k8s-resources/Deployment.tsx +++ b/cyclops-ui/src/components/k8s-resources/Deployment.tsx @@ -13,9 +13,6 @@ import { import { mapResponseError } from "../../utils/api/errors"; import PodTable from "./common/PodTable/PodTable"; import { isStreamingEnabled } from "../../utils/api/common"; -import { logStream } from "../../utils/api/sse/logs"; -import ReactAce from "react-ace/lib/ace"; -import { DownloadOutlined, ReadOutlined } from "@ant-design/icons"; import { useResourceListActions } from "./ResourceList/ResourceListActionsContext"; interface Props { @@ -24,7 +21,7 @@ interface Props { workload: any; } -export const Deployment = ({ name, namespace, workload }: Props) => { +const Deployment = ({ name, namespace, workload }: Props) => { const { fetchResource, streamingDisabled } = useResourceListActions(); const [loading, setLoading] = useState(true); @@ -120,260 +117,4 @@ export const Deployment = ({ name, namespace, workload }: Props) => { ); }; -export const DeploymentLogsButton = ({ name, namespace, workload }: Props) => { - const { streamingDisabled, getPodLogs, downloadPodLogs, streamPodLogs } = - useResourceListActions(); - const [logs, setLogs] = useState([]); - const [logsModal, setLogsModal] = useState({ - on: false, - containers: [], - initContainers: [], - }); - - const logsSignalControllerRef = useRef(null); - - const [error, setError] = useState({ - message: "", - description: "", - }); - - const handleCancelLogs = () => { - setLogsModal({ - on: false, - containers: [], - initContainers: [], - }); - setLogs([]); - - // send the abort signal - if (logsSignalControllerRef.current !== null) { - logsSignalControllerRef.current.abort(); - } - }; - - const getTabItems = () => { - let items: TabsProps["items"] = []; - - let container: any; - - if (logsModal.containers !== null) { - for (container of logsModal.containers) { - items.push({ - key: container.name, - label: container.name, - children: ( - - {downloadPodLogs ? ( -
- - -
- ) : ( - <> - )} - - - ), - }); - } - } - - if (logsModal.initContainers !== null) { - for (container of logsModal.initContainers) { - items.push({ - key: container.name, - label: "(init container) " + container.name, - children: ( - - {downloadPodLogs ? ( -
- - -
- ) : ( - <> - )} - - - ), - }); - } - } - - return items; - }; - - const onLogsTabsChange = () => { - const controller = new AbortController(); - if (logsSignalControllerRef.current !== null) { - logsSignalControllerRef.current.abort(); - } - logsSignalControllerRef.current = controller; // store the controller to be able to abort the request - setLogs(() => []); - - if (!streamingDisabled) { - logStream( - namespace, - name, - workload.pods[0].containers[0].name, - (log, isReset = false) => { - if (isReset) { - setLogs(() => []); - } else { - setLogs((prevLogs) => { - return [...prevLogs, log]; - }); - } - }, - (err, isReset = false) => { - if (isReset) { - setError({ - message: "", - description: "", - }); - } else { - setError(mapResponseError(err)); - } - }, - controller, - streamPodLogs, - ); - } else { - getPodLogs(namespace, name, workload.pods[0].containers[0].name) - .then((res) => { - if (res) { - setLogs(res); - } else { - setLogs(() => []); - } - }) - .catch((error) => { - setError(mapResponseError(error)); - }); - } - }; - - const downloadLogs = (container: string) => { - return () => downloadPodLogs(namespace, workload.pods[0].name, container); - }; - - return ( - <> - - - {error.message.length !== 0 && ( - { - setError({ - message: "", - description: "", - }); - }} - style={{ marginBottom: "20px" }} - /> - )} - - - - ); -}; +export default Deployment; diff --git a/cyclops-ui/src/components/k8s-resources/ResourceList/ResourceList.tsx b/cyclops-ui/src/components/k8s-resources/ResourceList/ResourceList.tsx index 01437cc1..28de60c2 100644 --- a/cyclops-ui/src/components/k8s-resources/ResourceList/ResourceList.tsx +++ b/cyclops-ui/src/components/k8s-resources/ResourceList/ResourceList.tsx @@ -19,17 +19,18 @@ import { ResourceRef, resourceRefKey, } from "../../../utils/resourceRef"; -import { Deployment, DeploymentLogsButton } from "../Deployment"; +import Deployment from "../Deployment"; import CronJob from "../CronJob"; import Job from "../Job"; import DaemonSet from "../DaemonSet"; -import { StatefulSet, StatefulSetLogsButton } from "../StatefulSet"; +import StatefulSet from "../StatefulSet"; import Pod from "../Pod"; import Service from "../Service"; import ClusterRole from "../ClusterRole"; import ConfigMap from "../ConfigMap"; import PersistentVolumeClaim from "../PersistentVolumeClaim"; import Secret from "../Secret"; +import ObjectLogsButton from "../common/ObjectLogsButton"; import { CaretRightOutlined, CheckCircleTwoTone, @@ -589,18 +590,10 @@ const ResourceList = ({ /> )} - {resource.kind === "Deployment" && ( + {(resource.kind === "Deployment" || + resource.kind === "StatefulSet") && ( - - - )} - {resource.kind === "StatefulSet" && ( - - { +const StatefulSet = ({ name, namespace, workload }: Props) => { const { fetchResource, streamingDisabled } = useResourceListActions(); const [loading, setLoading] = useState(true); @@ -120,259 +107,4 @@ export const StatefulSet = ({ name, namespace, workload }: Props) => { ); }; -export const StatefulSetLogsButton = ({ name, namespace, workload }: Props) => { - const { streamingDisabled, getPodLogs, downloadPodLogs, streamPodLogs } = - useResourceListActions(); - const [logs, setLogs] = useState([]); - const [logsModal, setLogsModal] = useState({ - on: false, - containers: [], - initContainers: [], - }); - - const logsSignalControllerRef = useRef(null); - - const [error, setError] = useState({ - message: "", - description: "", - }); - - const handleCancelLogs = () => { - setLogsModal({ - on: false, - containers: [], - initContainers: [], - }); - setLogs([]); - - // send the abort signal - if (logsSignalControllerRef.current !== null) { - logsSignalControllerRef.current.abort(); - } - }; - - const getTabItems = () => { - let items: TabsProps["items"] = []; - - let container: any; - - if (logsModal.containers !== null) { - for (container of logsModal.containers) { - items.push({ - key: container.name, - label: container.name, - children: ( - - {downloadPodLogs ? ( -
- - -
- ) : ( - <> - )} - - - ), - }); - } - } - - if (logsModal.initContainers !== null) { - for (container of logsModal.initContainers) { - items.push({ - key: container.name, - label: "(init container) " + container.name, - children: ( - - {downloadPodLogs ? ( -
- - -
- ) : ( - <> - )} - - - ), - }); - } - } - - return items; - }; - - const onLogsTabsChange = () => { - const controller = new AbortController(); - if (logsSignalControllerRef.current !== null) { - logsSignalControllerRef.current.abort(); - } - logsSignalControllerRef.current = controller; // store the controller to be able to abort the request - setLogs(() => []); - - if (!streamingDisabled) { - logStream( - namespace, - name, - workload.pods[0].containers[0].name, - (log, isReset = false) => { - if (isReset) { - setLogs(() => []); - } else { - setLogs((prevLogs) => { - return [...prevLogs, log]; - }); - } - }, - (err, isReset = false) => { - if (isReset) { - setError({ - message: "", - description: "", - }); - } else { - setError(mapResponseError(err)); - } - }, - controller, - streamPodLogs, - ); - } else { - getPodLogs(namespace, name, workload.pods[0].containers[0].name) - .then((res) => { - if (res) { - setLogs(res); - } else { - setLogs(() => []); - } - }) - .catch((error) => { - setError(mapResponseError(error)); - }); - } - }; - - const downloadLogs = (container: string) => { - return () => downloadPodLogs(namespace, workload.pods[0].name, container); - }; - - return ( - <> - - - {error.message.length !== 0 && ( - { - setError({ - message: "", - description: "", - }); - }} - style={{ marginBottom: "20px" }} - /> - )} - - - - ); -}; +export default StatefulSet; diff --git a/cyclops-ui/src/components/k8s-resources/common/ObjectLogsButton.tsx b/cyclops-ui/src/components/k8s-resources/common/ObjectLogsButton.tsx new file mode 100644 index 00000000..8d2e60eb --- /dev/null +++ b/cyclops-ui/src/components/k8s-resources/common/ObjectLogsButton.tsx @@ -0,0 +1,274 @@ +import { useState, useRef } from "react"; +import { Col, Divider, Alert, TabsProps, Button, Tabs, Modal } from "antd"; +import { logStream } from "../../../utils/api/sse/logs"; +import { mapResponseError } from "../../../utils/api/errors"; +import ReactAce from "react-ace/lib/ace"; +import { DownloadOutlined, ReadOutlined } from "@ant-design/icons"; + +import { useResourceListActions } from "../ResourceList/ResourceListActionsContext"; + +interface Props { + name: string; + namespace: string; + workload: any; +} + +const ObjectLogsButton = ({ name, namespace, workload }: Props) => { + const { streamingDisabled, getPodLogs, downloadPodLogs, streamPodLogs } = + useResourceListActions(); + const [logs, setLogs] = useState([]); + const [logsModal, setLogsModal] = useState({ + on: false, + containers: [], + initContainers: [], + }); + + const logsSignalControllerRef = useRef(null); + + const [error, setError] = useState({ + message: "", + description: "", + }); + + const handleCancelLogs = () => { + setLogsModal({ + on: false, + containers: [], + initContainers: [], + }); + setLogs([]); + + // send the abort signal + if (logsSignalControllerRef.current !== null) { + logsSignalControllerRef.current.abort(); + } + }; + + const getTabItems = () => { + let items: TabsProps["items"] = []; + + let container: any; + + if (logsModal.containers !== null) { + for (container of logsModal.containers) { + items.push({ + key: container.name, + label: container.name, + children: ( + + {downloadPodLogs ? ( +
+ + +
+ ) : ( + <> + )} + + + ), + }); + } + } + + if (logsModal.initContainers !== null) { + for (container of logsModal.initContainers) { + items.push({ + key: container.name, + label: "(init container) " + container.name, + children: ( + + {downloadPodLogs ? ( +
+ + +
+ ) : ( + <> + )} + + + ), + }); + } + } + + return items; + }; + + const onLogsTabsChange = () => { + const controller = new AbortController(); + if (logsSignalControllerRef.current !== null) { + logsSignalControllerRef.current.abort(); + } + logsSignalControllerRef.current = controller; // store the controller to be able to abort the request + setLogs(() => []); + + if (!streamingDisabled) { + logStream( + namespace, + name, + workload.pods[0].containers[0].name, + (log, isReset = false) => { + if (isReset) { + setLogs(() => []); + } else { + setLogs((prevLogs) => { + return [...prevLogs, log]; + }); + } + }, + (err, isReset = false) => { + if (isReset) { + setError({ + message: "", + description: "", + }); + } else { + setError(mapResponseError(err)); + } + }, + controller, + streamPodLogs, + ); + } else { + getPodLogs(namespace, name, workload.pods[0].containers[0].name) + .then((res) => { + if (res) { + setLogs(res); + } else { + setLogs(() => []); + } + }) + .catch((error) => { + setError(mapResponseError(error)); + }); + } + }; + + const downloadLogs = (container: string) => { + return () => downloadPodLogs(namespace, workload.pods[0].name, container); + }; + + return ( + <> + + + {error.message.length !== 0 && ( + { + setError({ + message: "", + description: "", + }); + }} + style={{ marginBottom: "20px" }} + /> + )} + + + + ); +}; + +export default ObjectLogsButton; From c3cd8a0701e330ef516062d830c4f20be3762b65 Mon Sep 17 00:00:00 2001 From: Sheikh-Abubaker Date: Mon, 10 Feb 2025 01:35:06 +0530 Subject: [PATCH 6/6] Remove unused imports Signed-off-by: Sheikh-Abubaker --- .../src/components/k8s-resources/Deployment.tsx | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/cyclops-ui/src/components/k8s-resources/Deployment.tsx b/cyclops-ui/src/components/k8s-resources/Deployment.tsx index 7a0823c0..2c2067ba 100644 --- a/cyclops-ui/src/components/k8s-resources/Deployment.tsx +++ b/cyclops-ui/src/components/k8s-resources/Deployment.tsx @@ -1,15 +1,5 @@ -import { useCallback, useEffect, useState, useRef } from "react"; -import { - Col, - Divider, - Row, - Alert, - Spin, - TabsProps, - Button, - Tabs, - Modal, -} from "antd"; +import { useCallback, useEffect, useState } from "react"; +import { Col, Divider, Row, Alert, Spin } from "antd"; import { mapResponseError } from "../../utils/api/errors"; import PodTable from "./common/PodTable/PodTable"; import { isStreamingEnabled } from "../../utils/api/common";