diff --git a/packages/app/src/KubernetesDashboardPage.tsx b/packages/app/src/KubernetesDashboardPage.tsx
index 4cff0f8b0..48ae73f4d 100644
--- a/packages/app/src/KubernetesDashboardPage.tsx
+++ b/packages/app/src/KubernetesDashboardPage.tsx
@@ -30,6 +30,7 @@ import HDXLineChart from './HDXLineChart';
import { withAppNav } from './layout';
import { LogTableWithSidePanel } from './LogTableWithSidePanel';
import MetricTagValueSelect from './MetricTagValueSelect';
+import NodeDetailsSidePanel from './NodeDetailsSidePanel';
import PodDetailsSidePanel from './PodDetailsSidePanel';
import HdxSearchInput from './SearchInput';
import SearchTimeRangePicker from './SearchTimeRangePicker';
@@ -410,6 +411,12 @@ const NodesTable = ({
seriesReturnType: 'column',
});
+ const getLink = React.useCallback((nodeName: string) => {
+ const searchParams = new URLSearchParams(window.location.search);
+ searchParams.set('nodeName', `${nodeName}`);
+ return window.location.pathname + '?' + searchParams.toString();
+ }, []);
+
const nodesList = React.useMemo(() => {
if (!data) {
return [];
@@ -472,30 +479,37 @@ const NodesTable = ({
) : (
{nodesList.map(node => (
-
- {node.name || 'N/A'} |
-
- {node.ready === 1 ? (
-
- Ready
-
- ) : (
-
- Not Ready
-
- )}
- |
-
- {formatNumber(
- node.cpuAvg,
- K8S_CPU_PERCENTAGE_NUMBER_FORMAT,
- )}
- |
-
- {formatNumber(node.memAvg, K8S_MEM_NUMBER_FORMAT)}
- |
- {node.uptime ? formatUptime(node.uptime) : '–'} |
-
+
+
+ {node.name || 'N/A'} |
+
+ {node.ready === 1 ? (
+
+ Ready
+
+ ) : (
+
+ Not Ready
+
+ )}
+ |
+
+ {formatNumber(
+ node.cpuAvg,
+ K8S_CPU_PERCENTAGE_NUMBER_FORMAT,
+ )}
+ |
+
+ {formatNumber(node.memAvg, K8S_MEM_NUMBER_FORMAT)}
+ |
+ {node.uptime ? formatUptime(node.uptime) : '–'} |
+
+
))}
)}
@@ -593,6 +607,7 @@ export default function KubernetesDashboardPage() {
Kubernetes Dashboard
+
{
+ if (!value) return null;
+ return (
+
+
+ {label}
+
+
+ {value}
+
+
+ );
+ },
+);
+
+const NodeDetails = ({
+ name,
+ dateRange,
+}: {
+ name: string;
+ dateRange: [Date, Date];
+}) => {
+ const where = `k8s.node.name:"${name}"`;
+ const groupBy = ['k8s.node.name'];
+
+ const { data } = api.useMultiSeriesChart({
+ series: [
+ {
+ table: 'metrics',
+ field: 'k8s.node.condition_ready - Gauge',
+ type: 'table',
+ aggFn: 'last_value',
+ where,
+ groupBy,
+ },
+ {
+ table: 'metrics',
+ field: 'k8s.node.uptime - Sum',
+ type: 'table',
+ aggFn: 'last_value',
+ where,
+ groupBy,
+ },
+ ],
+ endDate: dateRange[1] ?? new Date(),
+ startDate: dateRange[0] ?? new Date(),
+ seriesReturnType: 'column',
+ });
+
+ const properties = React.useMemo(() => {
+ const series: Record = data?.data?.[0] || {};
+ return {
+ ready: series['series_0.data'],
+ uptime: series['series_1.data'],
+ };
+ }, [data?.data]);
+
+ return (
+
+
+
+
+ Ready
+
+ ) : (
+
+ Not Ready
+
+ )
+ }
+ />
+
+
+
+ );
+};
+
+function NodeLogs({
+ where,
+ dateRange,
+}: {
+ where: string;
+ dateRange: [Date, Date];
+}) {
+ const [resultType, setResultType] = React.useState<'all' | 'error'>('all');
+
+ const _where = where + (resultType === 'error' ? ' level:err' : '');
+
+ return (
+
+
+
+ Latest Node Logs & Spans
+
+ {
+ if (value === 'all' || value === 'error') {
+ setResultType(value);
+ }
+ }}
+ data={[
+ { label: 'All', value: 'all' },
+ { label: 'Errors', value: 'error' },
+ ]}
+ />
+
+
+ Search
+
+
+
+
+
+
+ {}}
+ onPropertySearchClick={() => {}}
+ />
+
+
+ );
+}
+
+export default function NodeDetailsSidePanel() {
+ const [nodeName, setNodeName] = useQueryParam(
+ 'nodeName',
+ withDefault(StringParam, ''),
+ {
+ updateType: 'replaceIn',
+ },
+ );
+
+ const contextZIndex = useZIndex();
+ const drawerZIndex = contextZIndex + 10;
+
+ const where = React.useMemo(() => {
+ return `k8s.node.name:"${nodeName}"`;
+ }, [nodeName]);
+
+ const { searchedTimeRange: dateRange } = useTimeQuery({
+ isUTC: false,
+ defaultValue: 'Past 1h',
+ defaultTimeRange: [
+ defaultTimeRange?.[0]?.getTime() ?? -1,
+ defaultTimeRange?.[1]?.getTime() ?? -1,
+ ],
+ });
+
+ const handleClose = React.useCallback(() => {
+ setNodeName(undefined);
+ }, [setNodeName]);
+
+ if (!nodeName) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ CPU Usage by Pod
+
+
+
+
+
+
+
+
+
+ Memory Usage by Pod
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/packages/app/src/PodDetailsSidePanel.tsx b/packages/app/src/PodDetailsSidePanel.tsx
index b962163ff..a932435e5 100644
--- a/packages/app/src/PodDetailsSidePanel.tsx
+++ b/packages/app/src/PodDetailsSidePanel.tsx
@@ -162,8 +162,13 @@ export default function PodDetailsSidePanel() {
},
);
+ // If we're in a nested side panel, we need to use a higher z-index
+ // TODO: This is a hack
+ const [nodeName] = useQueryParam('nodeName', StringParam);
+ const [namespaceName] = useQueryParam('namespaceName', StringParam);
+ const isNested = !!nodeName || !!namespaceName;
const contextZIndex = useZIndex();
- const drawerZIndex = contextZIndex + 10;
+ const drawerZIndex = contextZIndex + 10 + (isNested ? 100 : 0);
const where = React.useMemo(() => {
return `k8s.pod.name:"${podName}"`;
@@ -194,7 +199,7 @@ export default function PodDetailsSidePanel() {
open={!!podName}
onClose={handleClose}
direction="right"
- size={'80vw'}
+ size={isNested ? '70vw' : '80vw'}
zIndex={drawerZIndex}
>