Refactor and clean up code across multiple components

- Simplified error handling in KubernetesEventsTab and KubernetesLogsTab by removing unnecessary line breaks.
- Consolidated import statements in KubernetesMetricsTab for better readability.
- Improved JSX formatting in KubernetesOverviewTab, KubernetesClusterContainerDetail, and other components for consistency.
- Enhanced code clarity in KubernetesObjectFetcher and KubernetesObjectParser by removing redundant line breaks and comments.
- Streamlined API response handling in IPWhitelistAPI for better readability.
- Updated PageSEO configuration for improved formatting.
This commit is contained in:
Nawaz Dhandala
2026-03-19 09:25:52 +00:00
parent f84df20610
commit 3efacce002
24 changed files with 303 additions and 315 deletions

View File

@@ -2,6 +2,7 @@ import React, { FunctionComponent, ReactElement, useState } from "react";
import Card from "Common/UI/Components/Card/Card";
import DictionaryOfStringsViewer from "Common/UI/Components/Dictionary/DictionaryOfStingsViewer";
import {
KubernetesContainerPort,
KubernetesContainerSpec,
KubernetesContainerStatus,
} from "../../Pages/Kubernetes/Utils/KubernetesObjectParser";
@@ -104,15 +105,19 @@ const ContainerCard: FunctionComponent<ContainerCardProps> = (
{props.container.ports.length > 0 && (
<div className="text-sm">
<span className="text-gray-500 font-medium">Ports:</span>{" "}
{props.container.ports.map((port, idx) => (
<span
key={idx}
className="inline-flex px-2 py-0.5 text-xs font-medium rounded bg-blue-50 text-blue-700 mr-1"
>
{port.name ? `${port.name}: ` : ""}
{port.containerPort}/{port.protocol}
</span>
))}
{props.container.ports.map(
(port: KubernetesContainerPort, idx: number) => {
return (
<span
key={idx}
className="inline-flex px-2 py-0.5 text-xs font-medium rounded bg-blue-50 text-blue-700 mr-1"
>
{port.name ? `${port.name}: ` : ""}
{port.containerPort}/{port.protocol}
</span>
);
},
)}
</div>
)}
@@ -176,20 +181,33 @@ const ContainerCard: FunctionComponent<ContainerCardProps> = (
</button>
{showMounts && (
<div className="mt-2 space-y-1">
{props.container.volumeMounts.map((mount, idx) => (
<div key={idx} className="text-sm flex gap-2">
<span className="font-medium text-gray-700">
{mount.name}
</span>
<span className="text-gray-500"></span>
<code className="text-xs bg-gray-100 px-1 py-0.5 rounded">
{mount.mountPath}
</code>
{mount.readOnly && (
<span className="text-xs text-gray-400">(read-only)</span>
)}
</div>
))}
{props.container.volumeMounts.map(
(
mount: {
name: string;
mountPath: string;
readOnly: boolean;
},
idx: number,
) => {
return (
<div key={idx} className="text-sm flex gap-2">
<span className="font-medium text-gray-700">
{mount.name}
</span>
<span className="text-gray-500"></span>
<code className="text-xs bg-gray-100 px-1 py-0.5 rounded">
{mount.mountPath}
</code>
{mount.readOnly && (
<span className="text-xs text-gray-400">
(read-only)
</span>
)}
</div>
);
},
)}
</div>
)}
</div>
@@ -202,10 +220,7 @@ const ContainerCard: FunctionComponent<ContainerCardProps> = (
const KubernetesContainersTab: FunctionComponent<ComponentProps> = (
props: ComponentProps,
): ReactElement => {
if (
props.containers.length === 0 &&
props.initContainers.length === 0
) {
if (props.containers.length === 0 && props.initContainers.length === 0) {
return (
<div className="text-gray-500 text-sm p-4">
No container information available.
@@ -223,32 +238,36 @@ const KubernetesContainersTab: FunctionComponent<ComponentProps> = (
const statuses: Array<KubernetesContainerStatus> | undefined = isInit
? props.initContainerStatuses
: props.containerStatuses;
return statuses?.find(
(s: KubernetesContainerStatus) => s.name === name,
);
return statuses?.find((s: KubernetesContainerStatus) => {
return s.name === name;
});
};
return (
<div className="space-y-4">
{props.initContainers.map(
(container: KubernetesContainerSpec, index: number) => (
<ContainerCard
key={`init-${index}`}
container={container}
status={getStatus(container.name, true)}
isInit={true}
/>
),
(container: KubernetesContainerSpec, index: number) => {
return (
<ContainerCard
key={`init-${index}`}
container={container}
status={getStatus(container.name, true)}
isInit={true}
/>
);
},
)}
{props.containers.map(
(container: KubernetesContainerSpec, index: number) => (
<ContainerCard
key={`container-${index}`}
container={container}
status={getStatus(container.name, false)}
isInit={false}
/>
),
(container: KubernetesContainerSpec, index: number) => {
return (
<ContainerCard
key={`container-${index}`}
container={container}
status={getStatus(container.name, false)}
isInit={false}
/>
);
},
)}
</div>
);

View File

@@ -29,18 +29,15 @@ const KubernetesEventsTab: FunctionComponent<ComponentProps> = (
const fetchEvents: () => Promise<void> = async (): Promise<void> => {
setIsLoading(true);
try {
const result: Array<KubernetesEvent> =
await fetchK8sEventsForResource({
clusterIdentifier: props.clusterIdentifier,
resourceKind: props.resourceKind,
resourceName: props.resourceName,
namespace: props.namespace,
});
const result: Array<KubernetesEvent> = await fetchK8sEventsForResource({
clusterIdentifier: props.clusterIdentifier,
resourceKind: props.resourceKind,
resourceName: props.resourceName,
namespace: props.namespace,
});
setEvents(result);
} catch (err) {
setError(
err instanceof Error ? err.message : "Failed to fetch events",
);
setError(err instanceof Error ? err.message : "Failed to fetch events");
}
setIsLoading(false);
};
@@ -91,8 +88,7 @@ const KubernetesEventsTab: FunctionComponent<ComponentProps> = (
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{events.map((event: KubernetesEvent, index: number) => {
const isWarning: boolean =
event.type.toLowerCase() === "warning";
const isWarning: boolean = event.type.toLowerCase() === "warning";
return (
<tr key={index} className={isWarning ? "bg-yellow-50" : ""}>
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-500">

View File

@@ -38,9 +38,7 @@ const KubernetesLogsTab: FunctionComponent<ComponentProps> = (
});
setLogs(result);
} catch (err) {
setError(
err instanceof Error ? err.message : "Failed to fetch logs",
);
setError(err instanceof Error ? err.message : "Failed to fetch logs");
}
setIsLoading(false);
};
@@ -65,8 +63,8 @@ const KubernetesLogsTab: FunctionComponent<ComponentProps> = (
return (
<div className="text-gray-500 text-sm p-4">
No application logs found for this pod in the last 6 hours. Logs will
appear here once the kubernetes-agent&apos;s filelog receiver is collecting
data.
appear here once the kubernetes-agent&apos;s filelog receiver is
collecting data.
</div>
);
}

View File

@@ -1,8 +1,4 @@
import React, {
FunctionComponent,
ReactElement,
useState,
} from "react";
import React, { FunctionComponent, ReactElement, useState } from "react";
import MetricView from "../../Components/Metrics/MetricView";
import MetricViewData from "Common/Types/Metrics/MetricViewData";
import MetricQueryConfigData from "Common/Types/Metrics/MetricQueryConfigData";

View File

@@ -44,23 +44,20 @@ const KubernetesOverviewTab: FunctionComponent<ComponentProps> = (
{/* Summary Info Cards */}
{props.summaryFields.length > 0 && (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{props.summaryFields.map(
(field: SummaryField, index: number) => {
return (
<InfoCard
key={index}
title={field.title}
value={field.value}
/>
);
},
)}
{props.summaryFields.map((field: SummaryField, index: number) => {
return (
<InfoCard key={index} title={field.title} value={field.value} />
);
})}
</div>
)}
{/* Owner References */}
{props.ownerReferences && props.ownerReferences.length > 0 && (
<Card title="Owner References" description="Resources that own this object.">
<Card
title="Owner References"
description="Resources that own this object."
>
<div className="space-y-1">
{props.ownerReferences.map(
(ref: { kind: string; name: string }, index: number) => {
@@ -147,7 +144,10 @@ const KubernetesOverviewTab: FunctionComponent<ComponentProps> = (
{/* Labels */}
{Object.keys(props.labels).length > 0 && (
<Card title="Labels" description="Key-value labels attached to this resource.">
<Card
title="Labels"
description="Key-value labels attached to this resource."
>
<DictionaryOfStringsViewer value={props.labels} />
</Card>
)}

View File

@@ -413,11 +413,17 @@ const DashboardLogsViewer: FunctionComponent<ComponentProps> = (
}
try {
// When live polling, recompute the time range so the query window
// slides forward to "now" and new logs become visible.
/*
* When live polling, recompute the time range so the query window
* slides forward to "now" and new logs become visible.
*/
let query: Query<Log> = filterOptions;
if (skipLoadingState && isLiveEnabled && timeRange.range !== TimeRange.CUSTOM) {
if (
skipLoadingState &&
isLiveEnabled &&
timeRange.range !== TimeRange.CUSTOM
) {
const freshRange: InBetween<Date> =
RangeStartAndEndDateTimeUtil.getStartAndEndDate(timeRange);
query = {
@@ -468,7 +474,16 @@ const DashboardLogsViewer: FunctionComponent<ComponentProps> = (
}
}
},
[filterOptions, isLiveEnabled, page, pageSize, select, sortField, sortOrder, timeRange],
[
filterOptions,
isLiveEnabled,
page,
pageSize,
select,
sortField,
sortOrder,
timeRange,
],
);
// --- Fetch histogram ---

View File

@@ -263,8 +263,7 @@ export async function fetchK8sEventsForResource(options: {
const objectKvList: JSONObject = objectVal;
// Get event details
const eventType: string =
getKvStringValue(objectKvList, "type") || "";
const eventType: string = getKvStringValue(objectKvList, "type") || "";
const reason: string = getKvStringValue(objectKvList, "reason") || "";
const note: string = getKvStringValue(objectKvList, "note") || "";
@@ -400,8 +399,7 @@ export async function fetchPodLogs(options: {
: "",
body: typeof log.body === "string" ? log.body : "",
severity: log.severityText || "INFO",
containerName:
(attrs["resource.k8s.container.name"] as string) || "",
containerName: (attrs["resource.k8s.container.name"] as string) || "",
};
});
} catch {

View File

@@ -1,8 +1,10 @@
import { JSONObject } from "Common/Types/JSON";
// ============================================================
// OTLP kvlistValue parsing helpers
// ============================================================
/*
* ============================================================
* OTLP kvlistValue parsing helpers
* ============================================================
*/
/**
* Extract a value from an OTLP kvlistValue by key.
@@ -141,9 +143,11 @@ export function getArrayValues(
.filter(Boolean) as Array<JSONObject>;
}
// ============================================================
// TypeScript interfaces for parsed K8s objects
// ============================================================
/*
* ============================================================
* TypeScript interfaces for parsed K8s objects
* ============================================================
*/
export interface KubernetesObjectMetadata {
name: string;
@@ -336,15 +340,14 @@ export interface KubernetesNamespaceObject {
};
}
// ============================================================
// Parsers
// ============================================================
/*
* ============================================================
* Parsers
* ============================================================
*/
function parseMetadata(kvList: JSONObject): KubernetesObjectMetadata {
const labelsKvList: string | JSONObject | null = getKvValue(
kvList,
"labels",
);
const labelsKvList: string | JSONObject | null = getKvValue(kvList, "labels");
const annotationsKvList: string | JSONObject | null = getKvValue(
kvList,
"annotations",
@@ -503,10 +506,7 @@ function parseContainerSpec(kvList: JSONObject): KubernetesContainerSpec {
resourcesKv,
"requests",
);
const limKv: string | JSONObject | null = getKvValue(
resourcesKv,
"limits",
);
const limKv: string | JSONObject | null = getKvValue(resourcesKv, "limits");
if (reqKv && typeof reqKv !== "string") {
requests = getKvListAsRecord(reqKv);
}
@@ -521,7 +521,8 @@ function parseContainerSpec(kvList: JSONObject): KubernetesContainerSpec {
);
const command: Array<string> = [];
if (commandArray && typeof commandArray !== "string") {
const cmdValues: Array<JSONObject> = (commandArray["values"] as Array<JSONObject>) || [];
const cmdValues: Array<JSONObject> =
(commandArray["values"] as Array<JSONObject>) || [];
for (const v of cmdValues) {
if (v["stringValue"]) {
command.push(v["stringValue"] as string);
@@ -532,7 +533,8 @@ function parseContainerSpec(kvList: JSONObject): KubernetesContainerSpec {
const argsArray: string | JSONObject | null = getKvValue(kvList, "args");
const args: Array<string> = [];
if (argsArray && typeof argsArray !== "string") {
const argValues: Array<JSONObject> = (argsArray["values"] as Array<JSONObject>) || [];
const argValues: Array<JSONObject> =
(argsArray["values"] as Array<JSONObject>) || [];
for (const v of argValues) {
if (v["stringValue"]) {
args.push(v["stringValue"] as string);
@@ -593,8 +595,7 @@ function parseContainerStatuses(
return {
name: getKvStringValue(kvList, "name"),
ready: getKvStringValue(kvList, "ready") === "true",
restartCount:
parseInt(getKvStringValue(kvList, "restartCount")) || 0,
restartCount: parseInt(getKvStringValue(kvList, "restartCount")) || 0,
state,
image: getKvStringValue(kvList, "image"),
};
@@ -618,10 +619,7 @@ export function parsePodObject(
}
const metadata: KubernetesObjectMetadata = parseMetadata(metadataKv);
const specKv: string | JSONObject | null = getKvValue(
objectKvList,
"spec",
);
const specKv: string | JSONObject | null = getKvValue(objectKvList, "spec");
const statusKv: string | JSONObject | null = getKvValue(
objectKvList,
"status",
@@ -683,8 +681,9 @@ export function parsePodObject(
| JSONObject
| undefined;
if (innerVal && innerVal["kvlistValue"]) {
const innerKv: JSONObject =
innerVal["kvlistValue"] as JSONObject;
const innerKv: JSONObject = innerVal[
"kvlistValue"
] as JSONObject;
volSource =
getKvStringValue(innerKv, "name") ||
getKvStringValue(innerKv, "path") ||
@@ -877,10 +876,12 @@ export function parseNodeObject(
);
if (addrArray && typeof addrArray !== "string") {
const addrItems: Array<JSONObject> = getArrayValues(addrArray);
addresses = addrItems.map((a: JSONObject) => ({
type: getKvStringValue(a, "type"),
address: getKvStringValue(a, "address"),
}));
addresses = addrItems.map((a: JSONObject) => {
return {
type: getKvStringValue(a, "type"),
address: getKvStringValue(a, "address"),
};
});
}
}
@@ -911,10 +912,7 @@ export function parseDeploymentObject(
return null;
}
const specKv: string | JSONObject | null = getKvValue(
objectKvList,
"spec",
);
const specKv: string | JSONObject | null = getKvValue(objectKvList, "spec");
const statusKv: string | JSONObject | null = getKvValue(
objectKvList,
"status",
@@ -953,8 +951,7 @@ export function parseDeploymentObject(
let unavailableReplicas: number = 0;
let conditions: Array<KubernetesCondition> = [];
if (statusKv && typeof statusKv !== "string") {
statusReplicas =
parseInt(getKvStringValue(statusKv, "replicas")) || 0;
statusReplicas = parseInt(getKvStringValue(statusKv, "replicas")) || 0;
readyReplicas =
parseInt(getKvStringValue(statusKv, "readyReplicas")) || 0;
availableReplicas =
@@ -996,10 +993,7 @@ export function parseStatefulSetObject(
return null;
}
const specKv: string | JSONObject | null = getKvValue(
objectKvList,
"spec",
);
const specKv: string | JSONObject | null = getKvValue(objectKvList, "spec");
const statusKv: string | JSONObject | null = getKvValue(
objectKvList,
"status",
@@ -1058,10 +1052,7 @@ export function parseDaemonSetObject(
return null;
}
const specKv: string | JSONObject | null = getKvValue(
objectKvList,
"spec",
);
const specKv: string | JSONObject | null = getKvValue(objectKvList, "spec");
const statusKv: string | JSONObject | null = getKvValue(
objectKvList,
"status",
@@ -1099,9 +1090,8 @@ export function parseDaemonSetObject(
) || 0
: 0,
numberReady: statusKv
? parseInt(
getKvStringValue(statusKv as JSONObject, "numberReady"),
) || 0
? parseInt(getKvStringValue(statusKv as JSONObject, "numberReady")) ||
0
: 0,
numberMisscheduled: statusKv
? parseInt(
@@ -1132,10 +1122,7 @@ export function parseJobObject(
return null;
}
const specKv: string | JSONObject | null = getKvValue(
objectKvList,
"spec",
);
const specKv: string | JSONObject | null = getKvValue(objectKvList, "spec");
const statusKv: string | JSONObject | null = getKvValue(
objectKvList,
"status",
@@ -1145,19 +1132,14 @@ export function parseJobObject(
metadata: parseMetadata(metadataKv),
spec: {
completions: specKv
? parseInt(
getKvStringValue(specKv as JSONObject, "completions"),
) || 0
? parseInt(getKvStringValue(specKv as JSONObject, "completions")) || 0
: 0,
parallelism: specKv
? parseInt(
getKvStringValue(specKv as JSONObject, "parallelism"),
) || 0
? parseInt(getKvStringValue(specKv as JSONObject, "parallelism")) || 0
: 0,
backoffLimit: specKv
? parseInt(
getKvStringValue(specKv as JSONObject, "backoffLimit"),
) || 0
? parseInt(getKvStringValue(specKv as JSONObject, "backoffLimit")) ||
0
: 0,
},
status: {
@@ -1165,9 +1147,7 @@ export function parseJobObject(
? parseInt(getKvStringValue(statusKv as JSONObject, "active")) || 0
: 0,
succeeded: statusKv
? parseInt(
getKvStringValue(statusKv as JSONObject, "succeeded"),
) || 0
? parseInt(getKvStringValue(statusKv as JSONObject, "succeeded")) || 0
: 0,
failed: statusKv
? parseInt(getKvStringValue(statusKv as JSONObject, "failed")) || 0
@@ -1205,10 +1185,7 @@ export function parseCronJobObject(
return null;
}
const specKv: string | JSONObject | null = getKvValue(
objectKvList,
"spec",
);
const specKv: string | JSONObject | null = getKvValue(objectKvList, "spec");
const statusKv: string | JSONObject | null = getKvValue(
objectKvList,
"status",
@@ -1220,10 +1197,9 @@ export function parseCronJobObject(
schedule: specKv
? getKvStringValue(specKv as JSONObject, "schedule")
: "",
suspend:
specKv
? getKvStringValue(specKv as JSONObject, "suspend") === "true"
: false,
suspend: specKv
? getKvStringValue(specKv as JSONObject, "suspend") === "true"
: false,
concurrencyPolicy: specKv
? getKvStringValue(specKv as JSONObject, "concurrencyPolicy")
: "",
@@ -1237,10 +1213,7 @@ export function parseCronJobObject(
: 0,
failedJobsHistoryLimit: specKv
? parseInt(
getKvStringValue(
specKv as JSONObject,
"failedJobsHistoryLimit",
),
getKvStringValue(specKv as JSONObject, "failedJobsHistoryLimit"),
) || 0
: 0,
},
@@ -1315,8 +1288,10 @@ export function extractObjectFromLogBody(
return objectVal;
}
// If no "object" key, the kvlist might BE the object (pull mode)
// Check if it has typical K8s fields
/*
* If no "object" key, the kvlist might BE the object (pull mode)
* Check if it has typical K8s fields
*/
const kind: string | JSONObject | null = getKvValue(topKvList, "kind");
if (kind) {
return topKvList;

View File

@@ -43,31 +43,30 @@ export default class KubernetesResourceUtils {
const endDate: Date = OneUptimeDate.getCurrentDate();
const startDate: Date = OneUptimeDate.addRemoveHours(endDate, -hoursBack);
const cpuResult: AggregatedResult =
await AnalyticsModelAPI.aggregate({
modelType: Metric,
aggregateBy: {
query: {
projectId: ProjectUtil.getCurrentProjectId()!,
time: new InBetween(startDate, endDate),
name: metricName,
attributes: {
"resource.k8s.cluster.name": clusterIdentifier,
...filterAttributes,
} as Dictionary<string | number | boolean>,
},
aggregationType: MetricsAggregationType.Avg,
aggregateColumnName: "value",
aggregationTimestampColumnName: "time",
startTimestamp: startDate,
endTimestamp: endDate,
limit: LIMIT_PER_PROJECT,
skip: 0,
groupBy: {
attributes: true,
},
const cpuResult: AggregatedResult = await AnalyticsModelAPI.aggregate({
modelType: Metric,
aggregateBy: {
query: {
projectId: ProjectUtil.getCurrentProjectId()!,
time: new InBetween(startDate, endDate),
name: metricName,
attributes: {
"resource.k8s.cluster.name": clusterIdentifier,
...filterAttributes,
} as Dictionary<string | number | boolean>,
},
});
aggregationType: MetricsAggregationType.Avg,
aggregateColumnName: "value",
aggregationTimestampColumnName: "time",
startTimestamp: startDate,
endTimestamp: endDate,
limit: LIMIT_PER_PROJECT,
skip: 0,
groupBy: {
attributes: true,
},
},
});
const resourceMap: Map<string, KubernetesResource> = new Map();
@@ -128,31 +127,30 @@ export default class KubernetesResourceUtils {
);
try {
const memoryResult: AggregatedResult =
await AnalyticsModelAPI.aggregate({
modelType: Metric,
aggregateBy: {
query: {
projectId: ProjectUtil.getCurrentProjectId()!,
time: new InBetween(startDate, endDate),
name: options.memoryMetricName,
attributes: {
"resource.k8s.cluster.name": options.clusterIdentifier,
...(options.filterAttributes || {}),
} as Dictionary<string | number | boolean>,
},
aggregationType: MetricsAggregationType.Avg,
aggregateColumnName: "value",
aggregationTimestampColumnName: "time",
startTimestamp: startDate,
endTimestamp: endDate,
limit: LIMIT_PER_PROJECT,
skip: 0,
groupBy: {
attributes: true,
},
const memoryResult: AggregatedResult = await AnalyticsModelAPI.aggregate({
modelType: Metric,
aggregateBy: {
query: {
projectId: ProjectUtil.getCurrentProjectId()!,
time: new InBetween(startDate, endDate),
name: options.memoryMetricName,
attributes: {
"resource.k8s.cluster.name": options.clusterIdentifier,
...(options.filterAttributes || {}),
} as Dictionary<string | number | boolean>,
},
});
aggregationType: MetricsAggregationType.Avg,
aggregateColumnName: "value",
aggregationTimestampColumnName: "time",
startTimestamp: startDate,
endTimestamp: endDate,
limit: LIMIT_PER_PROJECT,
skip: 0,
groupBy: {
attributes: true,
},
},
});
const memoryMap: Map<string, number> = new Map();
@@ -179,7 +177,7 @@ export default class KubernetesResourceUtils {
resource.memoryUsageBytes = memValue;
}
}
} catch (_err) {
} catch {
// Memory data is optional, don't fail if not available
}

View File

@@ -140,7 +140,10 @@ const KubernetesClusterContainerDetail: FunctionComponent<
children: (
<div className="p-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<InfoCard title="Container Name" value={containerName || "Unknown"} />
<InfoCard
title="Container Name"
value={containerName || "Unknown"}
/>
<InfoCard title="Cluster" value={clusterIdentifier} />
</div>
</div>
@@ -149,7 +152,10 @@ const KubernetesClusterContainerDetail: FunctionComponent<
{
name: "Logs",
children: (
<Card title="Container Logs" description="Logs for this container from the last 6 hours.">
<Card
title="Container Logs"
description="Logs for this container from the last 6 hours."
>
<KubernetesLogsTab
clusterIdentifier={clusterIdentifier}
podName=""
@@ -165,9 +171,7 @@ const KubernetesClusterContainerDetail: FunctionComponent<
title={`Container Metrics: ${containerName}`}
description="CPU and memory usage for this container over the last 6 hours."
>
<KubernetesMetricsTab
queryConfigs={[cpuQuery, memoryQuery]}
/>
<KubernetesMetricsTab queryConfigs={[cpuQuery, memoryQuery]} />
</Card>
),
},

View File

@@ -71,22 +71,21 @@ const KubernetesClusterCronJobDetail: FunctionComponent<
return;
}
const fetchCronJobObject: () => Promise<void> =
async (): Promise<void> => {
setIsLoadingObject(true);
try {
const obj: KubernetesCronJobObject | null =
await fetchLatestK8sObject<KubernetesCronJobObject>({
clusterIdentifier: cluster.clusterIdentifier || "",
resourceType: "cronjobs",
resourceName: cronJobName,
});
setCronJobObject(obj);
} catch {
// Graceful degradation — overview tab shows empty state
}
setIsLoadingObject(false);
};
const fetchCronJobObject: () => Promise<void> = async (): Promise<void> => {
setIsLoadingObject(true);
try {
const obj: KubernetesCronJobObject | null =
await fetchLatestK8sObject<KubernetesCronJobObject>({
clusterIdentifier: cluster.clusterIdentifier || "",
resourceType: "cronjobs",
resourceName: cronJobName,
});
setCronJobObject(obj);
} catch {
// Graceful degradation — overview tab shows empty state
}
setIsLoadingObject(false);
};
fetchCronJobObject().catch(() => {});
}, [cluster?.clusterIdentifier, cronJobName]);
@@ -228,7 +227,10 @@ const KubernetesClusterCronJobDetail: FunctionComponent<
{
name: "Events",
children: (
<Card title="CronJob Events" description="Kubernetes events for this cronjob in the last 24 hours.">
<Card
title="CronJob Events"
description="Kubernetes events for this cronjob in the last 24 hours."
>
<KubernetesEventsTab
clusterIdentifier={clusterIdentifier}
resourceKind="CronJob"
@@ -245,9 +247,7 @@ const KubernetesClusterCronJobDetail: FunctionComponent<
title={`CronJob Metrics: ${cronJobName}`}
description="CPU and memory usage for pods in this cronjob over the last 6 hours."
>
<KubernetesMetricsTab
queryConfigs={[cpuQuery, memoryQuery]}
/>
<KubernetesMetricsTab queryConfigs={[cpuQuery, memoryQuery]} />
</Card>
),
},

View File

@@ -239,9 +239,7 @@ const KubernetesClusterDaemonSetDetail: FunctionComponent<
title={`DaemonSet Metrics: ${daemonSetName}`}
description="CPU and memory usage for pods in this daemonset over the last 6 hours."
>
<KubernetesMetricsTab
queryConfigs={[cpuQuery, memoryQuery]}
/>
<KubernetesMetricsTab queryConfigs={[cpuQuery, memoryQuery]} />
</Card>
),
},

View File

@@ -236,9 +236,7 @@ const KubernetesClusterDeploymentDetail: FunctionComponent<
title={`Deployment Metrics: ${deploymentName}`}
description="CPU and memory usage for pods in this deployment over the last 6 hours."
>
<KubernetesMetricsTab
queryConfigs={[cpuQuery, memoryQuery]}
/>
<KubernetesMetricsTab queryConfigs={[cpuQuery, memoryQuery]} />
</Card>
),
},

View File

@@ -85,7 +85,9 @@ const KubernetesClusterDeployments: FunctionComponent<
resources={resources}
getViewRoute={(resource: KubernetesResource) => {
return RouteUtil.populateRouteParams(
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_DEPLOYMENT_DETAIL] as Route,
RouteMap[
PageMap.KUBERNETES_CLUSTER_VIEW_DEPLOYMENT_DETAIL
] as Route,
{
modelId: modelId,
subModelId: new ObjectID(resource.name),

View File

@@ -24,10 +24,7 @@ import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
import { JSONObject } from "Common/Types/JSON";
import InBetween from "Common/Types/BaseDatabase/InBetween";
import {
getKvValue,
getKvStringValue,
} from "../Utils/KubernetesObjectParser";
import { getKvValue, getKvStringValue } from "../Utils/KubernetesObjectParser";
import { KubernetesEvent } from "../Utils/KubernetesObjectFetcher";
const KubernetesClusterEvents: FunctionComponent<
@@ -131,12 +128,9 @@ const KubernetesClusterEvents: FunctionComponent<
}
const objectKvList: JSONObject = objectVal;
const eventType: string =
getKvStringValue(objectKvList, "type") || "";
const reason: string =
getKvStringValue(objectKvList, "reason") || "";
const note: string =
getKvStringValue(objectKvList, "note") || "";
const eventType: string = getKvStringValue(objectKvList, "type") || "";
const reason: string = getKvStringValue(objectKvList, "reason") || "";
const note: string = getKvStringValue(objectKvList, "note") || "";
// Get regarding object details using shared parser
const regardingKv: string | JSONObject | null = getKvValue(
@@ -160,17 +154,11 @@ const KubernetesClusterEvents: FunctionComponent<
"metadata",
);
const metadataObj: JSONObject | undefined =
metadataKv && typeof metadataKv !== "string"
? metadataKv
: undefined;
metadataKv && typeof metadataKv !== "string" ? metadataKv : undefined;
const namespace: string =
(regardingObj
? getKvStringValue(regardingObj, "namespace")
: "") ||
(metadataObj
? getKvStringValue(metadataObj, "namespace")
: "") ||
(regardingObj ? getKvStringValue(regardingObj, "namespace") : "") ||
(metadataObj ? getKvStringValue(metadataObj, "namespace") : "") ||
"";
if (eventType || reason) {
@@ -254,10 +242,7 @@ const KubernetesClusterEvents: FunctionComponent<
const isWarning: boolean =
event.type.toLowerCase() === "warning";
return (
<tr
key={index}
className={isWarning ? "bg-yellow-50" : ""}
>
<tr key={index} className={isWarning ? "bg-yellow-50" : ""}>
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
{event.timestamp}
</td>

View File

@@ -231,7 +231,10 @@ const KubernetesClusterJobDetail: FunctionComponent<
{
name: "Events",
children: (
<Card title="Job Events" description="Kubernetes events for this job in the last 24 hours.">
<Card
title="Job Events"
description="Kubernetes events for this job in the last 24 hours."
>
<KubernetesEventsTab
clusterIdentifier={clusterIdentifier}
resourceKind="Job"
@@ -248,9 +251,7 @@ const KubernetesClusterJobDetail: FunctionComponent<
title={`Job Metrics: ${jobName}`}
description="CPU and memory usage for pods in this job over the last 6 hours."
>
<KubernetesMetricsTab
queryConfigs={[cpuQuery, memoryQuery]}
/>
<KubernetesMetricsTab queryConfigs={[cpuQuery, memoryQuery]} />
</Card>
),
},

View File

@@ -200,7 +200,10 @@ const KubernetesClusterNamespaceDetail: FunctionComponent<
{
name: "Events",
children: (
<Card title="Namespace Events" description="Kubernetes events for this namespace in the last 24 hours.">
<Card
title="Namespace Events"
description="Kubernetes events for this namespace in the last 24 hours."
>
<KubernetesEventsTab
clusterIdentifier={clusterIdentifier}
resourceKind="Namespace"
@@ -216,9 +219,7 @@ const KubernetesClusterNamespaceDetail: FunctionComponent<
title={`Namespace Metrics: ${namespaceName}`}
description="CPU and memory usage for pods in this namespace over the last 6 hours."
>
<KubernetesMetricsTab
queryConfigs={[cpuQuery, memoryQuery]}
/>
<KubernetesMetricsTab queryConfigs={[cpuQuery, memoryQuery]} />
</Card>
),
},

View File

@@ -23,7 +23,10 @@ import { Tab } from "Common/UI/Components/Tabs/Tab";
import KubernetesOverviewTab from "../../../Components/Kubernetes/KubernetesOverviewTab";
import KubernetesEventsTab from "../../../Components/Kubernetes/KubernetesEventsTab";
import KubernetesMetricsTab from "../../../Components/Kubernetes/KubernetesMetricsTab";
import { KubernetesNodeObject } from "../Utils/KubernetesObjectParser";
import {
KubernetesCondition,
KubernetesNodeObject,
} from "../Utils/KubernetesObjectParser";
import { fetchLatestK8sObject } from "../Utils/KubernetesObjectFetcher";
const KubernetesClusterNodeDetail: FunctionComponent<
@@ -230,11 +233,10 @@ const KubernetesClusterNodeDetail: FunctionComponent<
if (!nodeObject) {
return { label: "Unknown", isReady: false };
}
const readyCondition = nodeObject.status.conditions.find(
(c) => {
const readyCondition: KubernetesCondition | undefined =
nodeObject.status.conditions.find((c: KubernetesCondition) => {
return c.type === "Ready";
},
);
});
if (readyCondition && readyCondition.status === "True") {
return { label: "Ready", isReady: true };
}
@@ -249,7 +251,7 @@ const KubernetesClusterNodeDetail: FunctionComponent<
];
if (nodeObject) {
const nodeStatus = getNodeStatus();
const nodeStatus: { label: string; isReady: boolean } = getNodeStatus();
summaryFields.push(
{

View File

@@ -294,7 +294,10 @@ const KubernetesClusterPodDetail: FunctionComponent<
{
name: "Events",
children: (
<Card title="Pod Events" description="Kubernetes events for this pod in the last 24 hours.">
<Card
title="Pod Events"
description="Kubernetes events for this pod in the last 24 hours."
>
<KubernetesEventsTab
clusterIdentifier={clusterIdentifier}
resourceKind="Pod"
@@ -307,7 +310,10 @@ const KubernetesClusterPodDetail: FunctionComponent<
{
name: "Logs",
children: (
<Card title="Application Logs" description="Container logs for this pod from the last 6 hours.">
<Card
title="Application Logs"
description="Container logs for this pod from the last 6 hours."
>
<KubernetesLogsTab
clusterIdentifier={clusterIdentifier}
podName={podName}

View File

@@ -239,9 +239,7 @@ const KubernetesClusterStatefulSetDetail: FunctionComponent<
title={`StatefulSet Metrics: ${statefulSetName}`}
description="CPU and memory usage for pods in this statefulset over the last 6 hours."
>
<KubernetesMetricsTab
queryConfigs={[cpuQuery, memoryQuery]}
/>
<KubernetesMetricsTab queryConfigs={[cpuQuery, memoryQuery]} />
</Card>
),
},
@@ -251,10 +249,7 @@ const KubernetesClusterStatefulSetDetail: FunctionComponent<
<Fragment>
<div className="mb-5">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-5">
<InfoCard
title="StatefulSet"
value={statefulSetName || "Unknown"}
/>
<InfoCard title="StatefulSet" value={statefulSetName || "Unknown"} />
<InfoCard title="Cluster" value={clusterIdentifier} />
</div>
</div>

View File

@@ -85,7 +85,9 @@ const KubernetesClusterStatefulSets: FunctionComponent<
resources={resources}
getViewRoute={(resource: KubernetesResource) => {
return RouteUtil.populateRouteParams(
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_STATEFULSET_DETAIL] as Route,
RouteMap[
PageMap.KUBERNETES_CLUSTER_VIEW_STATEFULSET_DETAIL
] as Route,
{
modelId: modelId,
subModelId: new ObjectID(resource.name),

View File

@@ -90,7 +90,10 @@ const DocsFeatureSet: FeatureSet = {
})
.join("\n")
: "- No IP addresses configured.";
contentInMarkdown = contentInMarkdown.replace("{{IP_WHITELIST}}", ipList);
contentInMarkdown = contentInMarkdown.replace(
"{{IP_WHITELIST}}",
ipList,
);
}
// Render Markdown content to HTML

View File

@@ -10,24 +10,21 @@ export default class IPWhitelistAPI {
public static init(): ExpressRouter {
const router: ExpressRouter = Express.getRouter();
router.get(
"/ip-whitelist",
(req: ExpressRequest, res: ExpressResponse) => {
const ipList: Array<string> = IpWhitelist
? IpWhitelist.split(",")
.map((ip: string) => {
return ip.trim();
})
.filter((ip: string) => {
return ip.length > 0;
})
: [];
router.get("/ip-whitelist", (req: ExpressRequest, res: ExpressResponse) => {
const ipList: Array<string> = IpWhitelist
? IpWhitelist.split(",")
.map((ip: string) => {
return ip.trim();
})
.filter((ip: string) => {
return ip.length > 0;
})
: [];
Response.sendJsonObjectResponse(req, res, {
ipWhitelist: ipList,
});
},
);
Response.sendJsonObjectResponse(req, res, {
ipWhitelist: ipList,
});
});
return router;
}

View File

@@ -439,8 +439,7 @@ export const PageSEOConfig: Record<string, PageSEOData> = {
},
"/product/scheduled-maintenance": {
title:
"Scheduled Maintenance | Plan & Communicate Downtime | OneUptime",
title: "Scheduled Maintenance | Plan & Communicate Downtime | OneUptime",
description:
"Plan, schedule, and communicate maintenance windows to your users. Notify subscribers automatically, update status pages in real-time. Open source maintenance management.",
canonicalPath: "/product/scheduled-maintenance",