From 8afcbc820781ed0b39b2b374767fd3728219c1fa Mon Sep 17 00:00:00 2001 From: amlannandy Date: Thu, 9 Jan 2025 12:36:16 +0530 Subject: [PATCH 1/3] feat: implement deployments in k8s infra monitoring --- .../infraMonitoring/getK8sDeploymentsList.ts | 70 +++ .../DeploymentDetails.interfaces.ts | 7 + .../DeploymentDetails.styles.scss | 247 ++++++++ .../DeploymentDetails/DeploymentDetails.tsx | 577 ++++++++++++++++++ .../Events/DeploymentEvents.styles.scss | 289 +++++++++ .../Events/DeploymentEvents.tsx | 363 +++++++++++ .../Events/NoEventsContainer.tsx | 16 + .../DeploymentDetails/Events/constants.ts | 65 ++ .../DeploymentDetails/Events/index.ts | 3 + .../Logs/DeploymentLogs.styles.scss | 133 ++++ .../DeploymentDetails/Logs/DeploymentLogs.tsx | 216 +++++++ .../Logs/DeploymentLogsDetailedView.tsx | 99 +++ .../Logs/NoLogsContainer.tsx | 16 + .../DeploymentDetails/Logs/constants.ts | 65 ++ .../DeploymentDetails/Logs/index.ts | 3 + .../Metrics/DeploymentMetrics.styles.scss | 45 ++ .../Metrics/DeploymentMetrics.tsx | 145 +++++ .../DeploymentDetails/Metrics/constants.ts | 544 +++++++++++++++++ .../DeploymentDetails/Metrics/index.ts | 3 + .../Traces/DeploymentTraces.styles.scss | 193 ++++++ .../Traces/DeploymentTraces.tsx | 199 ++++++ .../DeploymentDetails/Traces/constants.ts | 200 ++++++ .../DeploymentDetails/Traces/index.ts | 3 + .../DeploymentDetails/constants.ts | 6 + .../Deployments/DeploymentDetails/index.ts | 3 + .../K8sDeploymentsList.styles.scss | 17 + .../Deployments/K8sDeploymentsList.tsx | 518 ++++++++++++++++ .../InfraMonitoringK8s/Deployments/utils.tsx | 311 ++++++++++ .../InfraMonitoringK8s/InfraMonitoringK8s.tsx | 51 +- .../container/InfraMonitoringK8s/constants.ts | 35 +- .../useGetK8sDeploymentsList.ts | 48 ++ 31 files changed, 4468 insertions(+), 22 deletions(-) create mode 100644 frontend/src/api/infraMonitoring/getK8sDeploymentsList.ts create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.interfaces.ts create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.styles.scss create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.tsx create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Events/DeploymentEvents.styles.scss create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Events/DeploymentEvents.tsx create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Events/NoEventsContainer.tsx create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Events/constants.ts create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Events/index.ts create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Logs/DeploymentLogs.styles.scss create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Logs/DeploymentLogs.tsx create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Logs/DeploymentLogsDetailedView.tsx create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Logs/NoLogsContainer.tsx create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Logs/constants.ts create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Logs/index.ts create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Metrics/DeploymentMetrics.styles.scss create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Metrics/DeploymentMetrics.tsx create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Metrics/constants.ts create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Metrics/index.ts create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Traces/DeploymentTraces.styles.scss create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Traces/DeploymentTraces.tsx create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Traces/constants.ts create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/Traces/index.ts create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/constants.ts create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/index.ts create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/K8sDeploymentsList.styles.scss create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/K8sDeploymentsList.tsx create mode 100644 frontend/src/container/InfraMonitoringK8s/Deployments/utils.tsx create mode 100644 frontend/src/hooks/infraMonitoring/useGetK8sDeploymentsList.ts diff --git a/frontend/src/api/infraMonitoring/getK8sDeploymentsList.ts b/frontend/src/api/infraMonitoring/getK8sDeploymentsList.ts new file mode 100644 index 0000000000..9ce4643687 --- /dev/null +++ b/frontend/src/api/infraMonitoring/getK8sDeploymentsList.ts @@ -0,0 +1,70 @@ +import { ApiBaseInstance } from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { TagFilter } from 'types/api/queryBuilder/queryBuilderData'; + +export interface K8sDeploymentsListPayload { + filters: TagFilter; + groupBy?: BaseAutocompleteData[]; + offset?: number; + limit?: number; + orderBy?: { + columnName: string; + order: 'asc' | 'desc'; + }; +} + +export interface K8sDeploymentsData { + deploymentName: string; + cpuUsage: number; + memoryUsage: number; + desiredPods: number; + availablePods: number; + cpuRequest: number; + memoryRequest: number; + cpuLimit: number; + memoryLimit: number; + restarts: number; + meta: { + k8s_cluster_name: string; + k8s_deployment_name: string; + k8s_namespace_name: string; + }; +} + +export interface K8sDeploymentsListResponse { + status: string; + data: { + type: string; + records: K8sDeploymentsData[]; + groups: null; + total: number; + sentAnyHostMetricsData: boolean; + isSendingK8SAgentMetrics: boolean; + }; +} + +export const getK8sDeploymentsList = async ( + props: K8sDeploymentsListPayload, + signal?: AbortSignal, + headers?: Record, +): Promise | ErrorResponse> => { + try { + const response = await ApiBaseInstance.post('/deployments/list', props, { + signal, + headers, + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + params: props, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; diff --git a/frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.interfaces.ts b/frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.interfaces.ts new file mode 100644 index 0000000000..eb547aa343 --- /dev/null +++ b/frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.interfaces.ts @@ -0,0 +1,7 @@ +import { K8sDeploymentsData } from 'api/infraMonitoring/getK8sDeploymentsList'; + +export type DeploymentDetailsProps = { + deployment: K8sDeploymentsData | null; + isModalTimeSelection: boolean; + onClose: () => void; +}; diff --git a/frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.styles.scss b/frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.styles.scss new file mode 100644 index 0000000000..22d2934367 --- /dev/null +++ b/frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.styles.scss @@ -0,0 +1,247 @@ +.deployment-detail-drawer { + border-left: 1px solid var(--bg-slate-500); + background: var(--bg-ink-400); + box-shadow: -4px 10px 16px 2px rgba(0, 0, 0, 0.2); + + .ant-drawer-header { + padding: 8px 16px; + border-bottom: none; + + align-items: stretch; + + border-bottom: 1px solid var(--bg-slate-500); + background: var(--bg-ink-400); + } + + .ant-drawer-close { + margin-inline-end: 0px; + } + + .ant-drawer-body { + display: flex; + flex-direction: column; + padding: 16px; + } + + .title { + color: var(--text-vanilla-400); + font-family: 'Geist Mono'; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 20px; /* 142.857% */ + letter-spacing: -0.07px; + } + + .radio-button { + display: flex; + align-items: center; + justify-content: center; + padding-top: var(--padding-1); + border: 1px solid var(--bg-slate-400); + background: var(--bg-ink-300); + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1); + } + + .deployment-detail-drawer__deployment { + .deployment-details-grid { + .labels-row, + .values-row { + display: grid; + grid-template-columns: 1fr 1.5fr 1.5fr 1.5fr; + gap: 30px; + align-items: center; + } + + .labels-row { + margin-bottom: 8px; + } + + .deployment-details-metadata-label { + color: var(--text-vanilla-400); + font-family: Inter; + font-size: 11px; + font-style: normal; + font-weight: 500; + line-height: 18px; /* 163.636% */ + letter-spacing: 0.44px; + text-transform: uppercase; + } + + .deployment-details-metadata-value { + color: var(--text-vanilla-400); + font-family: 'Geist Mono'; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 20px; /* 142.857% */ + letter-spacing: -0.07px; + + width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .status-tag { + margin: 0; + + &.active { + color: var(--success-500); + background: var(--success-100); + border-color: var(--success-500); + } + + &.inactive { + color: var(--error-500); + background: var(--error-100); + border-color: var(--error-500); + } + } + + .progress-container { + width: 158px; + .ant-progress { + margin: 0; + + .ant-progress-text { + font-weight: 600; + } + } + } + + .ant-card { + &.ant-card-bordered { + border: 1px solid var(--bg-slate-500) !important; + } + } + } + } + + .tabs-and-search { + display: flex; + justify-content: space-between; + align-items: center; + margin: 16px 0; + + .action-btn { + border-radius: 2px; + border: 1px solid var(--bg-slate-400); + background: var(--bg-ink-300); + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1); + display: flex; + align-items: center; + justify-content: center; + } + } + + .views-tabs-container { + margin-top: 1.5rem; + display: flex; + justify-content: space-between; + align-items: center; + + .views-tabs { + color: var(--text-vanilla-400); + + .view-title { + display: flex; + gap: var(--margin-2); + align-items: center; + justify-content: center; + font-size: var(--font-size-xs); + font-style: normal; + font-weight: var(--font-weight-normal); + } + + .tab { + border: 1px solid var(--bg-slate-400); + width: 114px; + } + + .tab::before { + background: var(--bg-slate-400); + } + + .selected_view { + background: var(--bg-slate-300); + color: var(--text-vanilla-100); + border: 1px solid var(--bg-slate-400); + } + + .selected_view::before { + background: var(--bg-slate-400); + } + } + + .compass-button { + width: 30px; + height: 30px; + + border-radius: 2px; + border: 1px solid var(--bg-slate-400); + background: var(--bg-ink-300); + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1); + } + } + .ant-drawer-close { + padding: 0px; + } +} + +.lightMode { + .ant-drawer-header { + border-bottom: 1px solid var(--bg-vanilla-400); + background: var(--bg-vanilla-100); + } + + .deployment-detail-drawer { + .title { + color: var(--text-ink-300); + } + + .deployment-detail-drawer__deployment { + .ant-typography { + color: var(--text-ink-300); + background: transparent; + } + } + + .radio-button { + border: 1px solid var(--bg-vanilla-400); + background: var(--bg-vanilla-100); + color: var(--text-ink-300); + } + + .views-tabs { + .tab { + background: var(--bg-vanilla-100); + } + + .selected_view { + background: var(--bg-vanilla-300); + border: 1px solid var(--bg-slate-300); + color: var(--text-ink-400); + } + + .selected_view::before { + background: var(--bg-vanilla-300); + border-left: 1px solid var(--bg-slate-300); + } + } + + .compass-button { + border: 1px solid var(--bg-vanilla-300); + background: var(--bg-vanilla-100); + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1); + } + + .tabs-and-search { + .action-btn { + border: 1px solid var(--bg-vanilla-400); + background: var(--bg-vanilla-100); + color: var(--text-ink-300); + } + } + } +} diff --git a/frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.tsx b/frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.tsx new file mode 100644 index 0000000000..a9f7481c4c --- /dev/null +++ b/frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.tsx @@ -0,0 +1,577 @@ +/* eslint-disable sonarjs/no-identical-functions */ +import './DeploymentDetails.styles.scss'; + +import { Color, Spacing } from '@signozhq/design-tokens'; +import { Button, Divider, Drawer, Radio, Tooltip, Typography } from 'antd'; +import { RadioChangeEvent } from 'antd/lib'; +import logEvent from 'api/common/logEvent'; +import { VIEW_TYPES, VIEWS } from 'components/HostMetricsDetail/constants'; +import { QueryParams } from 'constants/query'; +import { + initialQueryBuilderFormValuesMap, + initialQueryState, +} from 'constants/queryBuilder'; +import ROUTES from 'constants/routes'; +import { + CustomTimeType, + Time, +} from 'container/TopNav/DateTimeSelectionV2/config'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import useUrlQuery from 'hooks/useUrlQuery'; +import GetMinMax from 'lib/getMinMax'; +import { + BarChart2, + ChevronsLeftRight, + Compass, + DraftingCompass, + ScrollText, + X, +} from 'lucide-react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { + IBuilderQuery, + TagFilterItem, +} from 'types/api/queryBuilder/queryBuilderData'; +import { + LogsAggregatorOperator, + TracesAggregatorOperator, +} from 'types/common/queryBuilder'; +import { GlobalReducer } from 'types/reducer/globalTime'; +import { v4 as uuidv4 } from 'uuid'; + +import { QUERY_KEYS } from './constants'; +import { DeploymentDetailsProps } from './DeploymentDetails.interfaces'; +import DeploymentEvents from './Events'; +import DeploymentLogs from './Logs'; +import DeploymentMetrics from './Metrics'; +import DeploymentTraces from './Traces'; + +function DeploymentDetails({ + deployment, + onClose, + isModalTimeSelection, +}: DeploymentDetailsProps): JSX.Element { + const { maxTime, minTime, selectedTime } = useSelector< + AppState, + GlobalReducer + >((state) => state.globalTime); + + const startMs = useMemo(() => Math.floor(Number(minTime) / 1000000000), [ + minTime, + ]); + const endMs = useMemo(() => Math.floor(Number(maxTime) / 1000000000), [ + maxTime, + ]); + + const urlQuery = useUrlQuery(); + + const [modalTimeRange, setModalTimeRange] = useState(() => ({ + startTime: startMs, + endTime: endMs, + })); + + const [selectedInterval, setSelectedInterval] = useState