mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: enhance handling of camelCase and snake_case in Kubernetes object parsing and telemetry
This commit is contained in:
@@ -31,21 +31,37 @@ export function getKvValue(
|
||||
if (!val) {
|
||||
return null;
|
||||
}
|
||||
// Handle both camelCase (JSON encoding) and snake_case (protobuf via protobufjs)
|
||||
if (val["stringValue"] !== undefined) {
|
||||
return val["stringValue"] as string;
|
||||
}
|
||||
if (val["string_value"] !== undefined) {
|
||||
return val["string_value"] as string;
|
||||
}
|
||||
if (val["intValue"] !== undefined) {
|
||||
return String(val["intValue"]);
|
||||
}
|
||||
if (val["int_value"] !== undefined) {
|
||||
return String(val["int_value"]);
|
||||
}
|
||||
if (val["boolValue"] !== undefined) {
|
||||
return String(val["boolValue"]);
|
||||
}
|
||||
if (val["bool_value"] !== undefined) {
|
||||
return String(val["bool_value"]);
|
||||
}
|
||||
if (val["kvlistValue"]) {
|
||||
return val["kvlistValue"] as JSONObject;
|
||||
}
|
||||
if (val["kvlist_value"]) {
|
||||
return val["kvlist_value"] as JSONObject;
|
||||
}
|
||||
if (val["arrayValue"]) {
|
||||
return val["arrayValue"] as JSONObject;
|
||||
}
|
||||
if (val["array_value"]) {
|
||||
return val["array_value"] as JSONObject;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -105,10 +121,16 @@ export function getKvListAsRecord(
|
||||
if (key && val) {
|
||||
if (val["stringValue"] !== undefined) {
|
||||
result[key] = val["stringValue"] as string;
|
||||
} else if (val["string_value"] !== undefined) {
|
||||
result[key] = val["string_value"] as string;
|
||||
} else if (val["intValue"] !== undefined) {
|
||||
result[key] = String(val["intValue"]);
|
||||
} else if (val["int_value"] !== undefined) {
|
||||
result[key] = String(val["int_value"]);
|
||||
} else if (val["boolValue"] !== undefined) {
|
||||
result[key] = String(val["boolValue"]);
|
||||
} else if (val["bool_value"] !== undefined) {
|
||||
result[key] = String(val["bool_value"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,9 +157,15 @@ export function getArrayValues(
|
||||
if (item["kvlistValue"]) {
|
||||
return item["kvlistValue"] as JSONObject;
|
||||
}
|
||||
if (item["kvlist_value"]) {
|
||||
return item["kvlist_value"] as JSONObject;
|
||||
}
|
||||
if (item["stringValue"]) {
|
||||
return item as JSONObject;
|
||||
}
|
||||
if (item["string_value"]) {
|
||||
return item as JSONObject;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Boolean) as Array<JSONObject>;
|
||||
@@ -148,24 +176,43 @@ export function getArrayValues(
|
||||
* Handles stringValue, intValue, boolValue, kvlistValue, and arrayValue.
|
||||
*/
|
||||
function convertOtlpValue(valueWrapper: JSONObject): unknown {
|
||||
// Handle both camelCase (JSON encoding) and snake_case (protobuf via protobufjs)
|
||||
if (valueWrapper["stringValue"] !== undefined) {
|
||||
return valueWrapper["stringValue"];
|
||||
}
|
||||
if (valueWrapper["string_value"] !== undefined) {
|
||||
return valueWrapper["string_value"];
|
||||
}
|
||||
if (valueWrapper["intValue"] !== undefined) {
|
||||
return Number(valueWrapper["intValue"]);
|
||||
}
|
||||
if (valueWrapper["int_value"] !== undefined) {
|
||||
return Number(valueWrapper["int_value"]);
|
||||
}
|
||||
if (valueWrapper["boolValue"] !== undefined) {
|
||||
return valueWrapper["boolValue"];
|
||||
}
|
||||
if (valueWrapper["bool_value"] !== undefined) {
|
||||
return valueWrapper["bool_value"];
|
||||
}
|
||||
if (valueWrapper["doubleValue"] !== undefined) {
|
||||
return Number(valueWrapper["doubleValue"]);
|
||||
}
|
||||
if (valueWrapper["double_value"] !== undefined) {
|
||||
return Number(valueWrapper["double_value"]);
|
||||
}
|
||||
if (valueWrapper["kvlistValue"]) {
|
||||
return kvListToPlainObject(valueWrapper["kvlistValue"] as JSONObject);
|
||||
}
|
||||
if (valueWrapper["kvlist_value"]) {
|
||||
return kvListToPlainObject(valueWrapper["kvlist_value"] as JSONObject);
|
||||
}
|
||||
if (valueWrapper["arrayValue"]) {
|
||||
return convertOtlpArray(valueWrapper["arrayValue"] as JSONObject);
|
||||
}
|
||||
if (valueWrapper["array_value"]) {
|
||||
return convertOtlpArray(valueWrapper["array_value"] as JSONObject);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1861,9 +1908,9 @@ export function extractObjectFromLogBody(
|
||||
): JSONObject | null {
|
||||
try {
|
||||
const bodyObj: JSONObject = JSON.parse(bodyString) as JSONObject;
|
||||
const topKvList: JSONObject | undefined = bodyObj["kvlistValue"] as
|
||||
| JSONObject
|
||||
| undefined;
|
||||
// Handle both camelCase (JSON encoding) and snake_case (protobuf via protobufjs)
|
||||
const topKvList: JSONObject | undefined = (bodyObj["kvlistValue"] ||
|
||||
bodyObj["kvlist_value"]) as JSONObject | undefined;
|
||||
if (!topKvList) {
|
||||
return null;
|
||||
}
|
||||
@@ -1886,6 +1933,15 @@ export function extractObjectFromLogBody(
|
||||
return topKvList;
|
||||
}
|
||||
|
||||
// Also check "metadata" as a fallback for objects without "kind"
|
||||
const metadata: string | JSONObject | null = getKvValue(
|
||||
topKvList,
|
||||
"metadata",
|
||||
);
|
||||
if (metadata && typeof metadata !== "string") {
|
||||
return topKvList;
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
|
||||
@@ -45,12 +45,17 @@ import StatusBadge, {
|
||||
StatusBadgeType,
|
||||
} from "Common/UI/Components/StatusBadge/StatusBadge";
|
||||
import ResourceUsageBar from "Common/UI/Components/ResourceUsageBar/ResourceUsageBar";
|
||||
import Icon from "Common/UI/Components/Icon/Icon";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
|
||||
interface ResourceLink {
|
||||
title: string;
|
||||
description: string;
|
||||
pageMap: PageMap;
|
||||
count?: number | undefined;
|
||||
icon: IconProp;
|
||||
iconBgClass: string;
|
||||
iconTextClass: string;
|
||||
}
|
||||
|
||||
function formatRelativeTime(timestamp: string): string {
|
||||
@@ -318,64 +323,97 @@ const KubernetesClusterOverview: FunctionComponent<
|
||||
const workloadLinks: Array<ResourceLink> = [
|
||||
{
|
||||
title: "Namespaces",
|
||||
description: "View all namespaces",
|
||||
description: "Logical partitions for resources",
|
||||
pageMap: PageMap.KUBERNETES_CLUSTER_VIEW_NAMESPACES,
|
||||
count: namespaceCount > 0 ? namespaceCount : undefined,
|
||||
icon: IconProp.Folder,
|
||||
iconBgClass: "bg-indigo-100",
|
||||
iconTextClass: "text-indigo-600",
|
||||
},
|
||||
{
|
||||
title: "Pods",
|
||||
description: "View all pods",
|
||||
description: "Smallest deployable units",
|
||||
pageMap: PageMap.KUBERNETES_CLUSTER_VIEW_PODS,
|
||||
count: podCount > 0 ? podCount : undefined,
|
||||
icon: IconProp.Circle,
|
||||
iconBgClass: "bg-emerald-100",
|
||||
iconTextClass: "text-emerald-600",
|
||||
},
|
||||
{
|
||||
title: "Deployments",
|
||||
description: "View all deployments",
|
||||
description: "Manage replica sets and rollouts",
|
||||
pageMap: PageMap.KUBERNETES_CLUSTER_VIEW_DEPLOYMENTS,
|
||||
icon: IconProp.Layers,
|
||||
iconBgClass: "bg-blue-100",
|
||||
iconTextClass: "text-blue-600",
|
||||
},
|
||||
{
|
||||
title: "StatefulSets",
|
||||
description: "View all statefulsets",
|
||||
description: "Ordered, stateful pod management",
|
||||
pageMap: PageMap.KUBERNETES_CLUSTER_VIEW_STATEFULSETS,
|
||||
icon: IconProp.Database,
|
||||
iconBgClass: "bg-purple-100",
|
||||
iconTextClass: "text-purple-600",
|
||||
},
|
||||
{
|
||||
title: "DaemonSets",
|
||||
description: "View all daemonsets",
|
||||
description: "Run pods on every node",
|
||||
pageMap: PageMap.KUBERNETES_CLUSTER_VIEW_DAEMONSETS,
|
||||
icon: IconProp.Settings,
|
||||
iconBgClass: "bg-orange-100",
|
||||
iconTextClass: "text-orange-600",
|
||||
},
|
||||
{
|
||||
title: "Jobs",
|
||||
description: "View all jobs",
|
||||
description: "Run-to-completion workloads",
|
||||
pageMap: PageMap.KUBERNETES_CLUSTER_VIEW_JOBS,
|
||||
icon: IconProp.Play,
|
||||
iconBgClass: "bg-amber-100",
|
||||
iconTextClass: "text-amber-600",
|
||||
},
|
||||
{
|
||||
title: "CronJobs",
|
||||
description: "View all cron jobs",
|
||||
description: "Scheduled recurring tasks",
|
||||
pageMap: PageMap.KUBERNETES_CLUSTER_VIEW_CRONJOBS,
|
||||
icon: IconProp.Clock,
|
||||
iconBgClass: "bg-teal-100",
|
||||
iconTextClass: "text-teal-600",
|
||||
},
|
||||
];
|
||||
|
||||
const infraLinks: Array<ResourceLink> = [
|
||||
{
|
||||
title: "Nodes",
|
||||
description: "View all nodes",
|
||||
description: "Worker machines in the cluster",
|
||||
pageMap: PageMap.KUBERNETES_CLUSTER_VIEW_NODES,
|
||||
count: nodeCount > 0 ? nodeCount : undefined,
|
||||
icon: IconProp.Server,
|
||||
iconBgClass: "bg-slate-100",
|
||||
iconTextClass: "text-slate-600",
|
||||
},
|
||||
{
|
||||
title: "Containers",
|
||||
description: "View all containers",
|
||||
description: "Running container instances",
|
||||
pageMap: PageMap.KUBERNETES_CLUSTER_VIEW_CONTAINERS,
|
||||
icon: IconProp.Cube,
|
||||
iconBgClass: "bg-cyan-100",
|
||||
iconTextClass: "text-cyan-600",
|
||||
},
|
||||
{
|
||||
title: "PVCs",
|
||||
description: "View persistent volume claims",
|
||||
description: "Persistent volume claims",
|
||||
pageMap: PageMap.KUBERNETES_CLUSTER_VIEW_PVCS,
|
||||
icon: IconProp.Disc,
|
||||
iconBgClass: "bg-rose-100",
|
||||
iconTextClass: "text-rose-600",
|
||||
},
|
||||
{
|
||||
title: "PVs",
|
||||
description: "View persistent volumes",
|
||||
description: "Persistent volumes",
|
||||
pageMap: PageMap.KUBERNETES_CLUSTER_VIEW_PVS,
|
||||
icon: IconProp.Disc,
|
||||
iconBgClass: "bg-fuchsia-100",
|
||||
iconTextClass: "text-fuchsia-600",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -428,7 +466,7 @@ const KubernetesClusterOverview: FunctionComponent<
|
||||
links: Array<ResourceLink>,
|
||||
): ReactElement => {
|
||||
return (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 p-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 py-4 pr-4 pl-1">
|
||||
{links.map((link: ResourceLink) => {
|
||||
return (
|
||||
<div
|
||||
@@ -441,19 +479,29 @@ const KubernetesClusterOverview: FunctionComponent<
|
||||
),
|
||||
);
|
||||
}}
|
||||
className="flex items-center justify-between p-3 rounded-lg border border-gray-200 hover:border-indigo-300 hover:bg-indigo-50/50 transition-all duration-150 group cursor-pointer"
|
||||
className="flex items-center gap-3 p-4 rounded-xl border border-gray-200 hover:border-indigo-300 hover:shadow-md transition-all duration-200 group cursor-pointer"
|
||||
>
|
||||
<div>
|
||||
<div className="font-medium text-gray-900 group-hover:text-indigo-700">
|
||||
{link.title}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">{link.description}</div>
|
||||
<div
|
||||
className={`flex-shrink-0 w-10 h-10 rounded-lg flex items-center justify-center ${link.iconBgClass}`}
|
||||
>
|
||||
<Icon
|
||||
icon={link.icon}
|
||||
className={`h-5 w-5 ${link.iconTextClass}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="font-medium text-gray-900 group-hover:text-indigo-700 flex items-center justify-between">
|
||||
<span>{link.title}</span>
|
||||
{link.count !== undefined && (
|
||||
<span className="inline-flex items-center justify-center min-w-[1.5rem] h-6 px-2 text-xs font-semibold rounded-full bg-indigo-100 text-indigo-700">
|
||||
{link.count}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 mt-0.5">
|
||||
{link.description}
|
||||
</div>
|
||||
</div>
|
||||
{link.count !== undefined && (
|
||||
<span className="inline-flex items-center justify-center min-w-[1.5rem] h-6 px-2 text-xs font-semibold rounded-full bg-indigo-100 text-indigo-700">
|
||||
{link.count}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@@ -524,6 +572,14 @@ const KubernetesClusterOverview: FunctionComponent<
|
||||
/>
|
||||
<InfoCard
|
||||
title="Nodes"
|
||||
onClick={() => {
|
||||
Navigation.navigate(
|
||||
RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_NODES] as Route,
|
||||
{ modelId: modelId },
|
||||
),
|
||||
);
|
||||
}}
|
||||
value={
|
||||
<span className="text-2xl font-semibold">
|
||||
{nodeCount.toString()}
|
||||
@@ -537,6 +593,14 @@ const KubernetesClusterOverview: FunctionComponent<
|
||||
/>
|
||||
<InfoCard
|
||||
title="Pods"
|
||||
onClick={() => {
|
||||
Navigation.navigate(
|
||||
RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_PODS] as Route,
|
||||
{ modelId: modelId },
|
||||
),
|
||||
);
|
||||
}}
|
||||
value={
|
||||
<span className="text-2xl font-semibold">
|
||||
{podCount.toString()}
|
||||
@@ -545,6 +609,14 @@ const KubernetesClusterOverview: FunctionComponent<
|
||||
/>
|
||||
<InfoCard
|
||||
title="Namespaces"
|
||||
onClick={() => {
|
||||
Navigation.navigate(
|
||||
RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_NAMESPACES] as Route,
|
||||
{ modelId: modelId },
|
||||
),
|
||||
);
|
||||
}}
|
||||
value={
|
||||
<span className="text-2xl font-semibold">
|
||||
{namespaceCount.toString()}
|
||||
|
||||
@@ -17,6 +17,10 @@ import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI";
|
||||
import KubernetesResourceUtils, {
|
||||
KubernetesResource,
|
||||
} from "../Utils/KubernetesResourceUtils";
|
||||
import {
|
||||
fetchK8sObjectsBatch,
|
||||
KubernetesObjectType,
|
||||
} from "../Utils/KubernetesObjectFetcher";
|
||||
|
||||
const KubernetesClusterViewLayout: FunctionComponent<
|
||||
PageComponentProps
|
||||
@@ -104,6 +108,27 @@ const KubernetesClusterViewLayout: FunctionComponent<
|
||||
}),
|
||||
]);
|
||||
|
||||
// Fetch PV/PVC/HPA/VPA counts from k8sobjects logs (they don't have k8s_cluster metrics)
|
||||
const [pvcs, pvs, hpas, vpas]: Array<Map<string, KubernetesObjectType>> =
|
||||
await Promise.all([
|
||||
fetchK8sObjectsBatch({
|
||||
clusterIdentifier: ci,
|
||||
resourceType: "persistentvolumeclaims",
|
||||
}),
|
||||
fetchK8sObjectsBatch({
|
||||
clusterIdentifier: ci,
|
||||
resourceType: "persistentvolumes",
|
||||
}),
|
||||
fetchK8sObjectsBatch({
|
||||
clusterIdentifier: ci,
|
||||
resourceType: "horizontalpodautoscalers",
|
||||
}),
|
||||
fetchK8sObjectsBatch({
|
||||
clusterIdentifier: ci,
|
||||
resourceType: "verticalpodautoscalers",
|
||||
}),
|
||||
]);
|
||||
|
||||
setResourceCounts({
|
||||
nodes: nodes?.length ?? 0,
|
||||
pods: pods?.length ?? 0,
|
||||
@@ -114,6 +139,10 @@ const KubernetesClusterViewLayout: FunctionComponent<
|
||||
jobs: jobs?.length ?? 0,
|
||||
cronJobs: cronJobs?.length ?? 0,
|
||||
containers: containers?.length ?? 0,
|
||||
pvcs: pvcs?.size ?? 0,
|
||||
pvs: pvs?.size ?? 0,
|
||||
hpas: hpas?.size ?? 0,
|
||||
vpas: vpas?.size ?? 0,
|
||||
});
|
||||
} catch {
|
||||
// Counts are supplementary, don't fail the layout
|
||||
|
||||
@@ -211,33 +211,49 @@ export default class TelemetryUtil {
|
||||
const jsonValue: JSONObject = value as JSONObject;
|
||||
|
||||
if (jsonValue && typeof jsonValue === "object") {
|
||||
if (Object.prototype.hasOwnProperty.call(jsonValue, "stringValue")) {
|
||||
const stringValue: JSONValue = jsonValue["stringValue"];
|
||||
// Handle both camelCase (JSON encoding) and snake_case (protobuf via protobufjs)
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(jsonValue, "stringValue") ||
|
||||
Object.prototype.hasOwnProperty.call(jsonValue, "string_value")
|
||||
) {
|
||||
const stringValue: JSONValue =
|
||||
jsonValue["stringValue"] ?? jsonValue["string_value"];
|
||||
finalObj =
|
||||
stringValue !== undefined && stringValue !== null
|
||||
? (stringValue as string)
|
||||
: "";
|
||||
} else if (Object.prototype.hasOwnProperty.call(jsonValue, "intValue")) {
|
||||
const intValue: JSONValue = jsonValue["intValue"];
|
||||
} else if (
|
||||
Object.prototype.hasOwnProperty.call(jsonValue, "intValue") ||
|
||||
Object.prototype.hasOwnProperty.call(jsonValue, "int_value")
|
||||
) {
|
||||
const intValue: JSONValue =
|
||||
jsonValue["intValue"] ?? jsonValue["int_value"];
|
||||
if (intValue !== undefined && intValue !== null) {
|
||||
finalObj = intValue as number;
|
||||
}
|
||||
} else if (
|
||||
Object.prototype.hasOwnProperty.call(jsonValue, "doubleValue")
|
||||
Object.prototype.hasOwnProperty.call(jsonValue, "doubleValue") ||
|
||||
Object.prototype.hasOwnProperty.call(jsonValue, "double_value")
|
||||
) {
|
||||
const doubleValue: JSONValue = jsonValue["doubleValue"];
|
||||
const doubleValue: JSONValue =
|
||||
jsonValue["doubleValue"] ?? jsonValue["double_value"];
|
||||
if (doubleValue !== undefined && doubleValue !== null) {
|
||||
finalObj = doubleValue as number;
|
||||
}
|
||||
} else if (Object.prototype.hasOwnProperty.call(jsonValue, "boolValue")) {
|
||||
finalObj = jsonValue["boolValue"] as boolean;
|
||||
} else if (
|
||||
jsonValue["arrayValue"] &&
|
||||
(jsonValue["arrayValue"] as JSONObject)["values"]
|
||||
Object.prototype.hasOwnProperty.call(jsonValue, "boolValue") ||
|
||||
Object.prototype.hasOwnProperty.call(jsonValue, "bool_value")
|
||||
) {
|
||||
const values: JSONArray = (jsonValue["arrayValue"] as JSONObject)[
|
||||
"values"
|
||||
] as JSONArray;
|
||||
finalObj = (jsonValue["boolValue"] ?? jsonValue["bool_value"]) as boolean;
|
||||
} else if (
|
||||
(jsonValue["arrayValue"] &&
|
||||
(jsonValue["arrayValue"] as JSONObject)["values"]) ||
|
||||
(jsonValue["array_value"] &&
|
||||
(jsonValue["array_value"] as JSONObject)["values"])
|
||||
) {
|
||||
const arrayVal: JSONObject = (jsonValue["arrayValue"] ||
|
||||
jsonValue["array_value"]) as JSONObject;
|
||||
const values: JSONArray = arrayVal["values"] as JSONArray;
|
||||
finalObj = values.map((v: JSONObject) => {
|
||||
return this.getAttributeValues(
|
||||
prefixKeysWithString,
|
||||
@@ -290,17 +306,19 @@ export default class TelemetryUtil {
|
||||
|
||||
finalObj = flattenedFields;
|
||||
} else if (
|
||||
jsonValue["kvlistValue"] &&
|
||||
(jsonValue["kvlistValue"] as JSONObject)["values"]
|
||||
(jsonValue["kvlistValue"] &&
|
||||
(jsonValue["kvlistValue"] as JSONObject)["values"]) ||
|
||||
(jsonValue["kvlist_value"] &&
|
||||
(jsonValue["kvlist_value"] as JSONObject)["values"])
|
||||
) {
|
||||
const values: JSONArray = (jsonValue["kvlistValue"] as JSONObject)[
|
||||
"values"
|
||||
] as JSONArray;
|
||||
const kvlistVal: JSONObject = (jsonValue["kvlistValue"] ||
|
||||
jsonValue["kvlist_value"]) as JSONObject;
|
||||
const values: JSONArray = kvlistVal["values"] as JSONArray;
|
||||
finalObj = this.getAttributes({
|
||||
prefixKeysWithString,
|
||||
items: values,
|
||||
});
|
||||
} else if ("nullValue" in jsonValue) {
|
||||
} else if ("nullValue" in jsonValue || "null_value" in jsonValue) {
|
||||
finalObj = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface ComponentProps {
|
||||
value: string | ReactElement;
|
||||
className?: string;
|
||||
textClassName?: string;
|
||||
onClick?: (() => void) | undefined;
|
||||
}
|
||||
|
||||
const InfoCard: FunctionComponent<ComponentProps> = (
|
||||
@@ -13,7 +14,8 @@ const InfoCard: FunctionComponent<ComponentProps> = (
|
||||
): ReactElement => {
|
||||
return (
|
||||
<div
|
||||
className={`rounded-xl bg-white border border-gray-200 shadow-sm hover:shadow-md transition-shadow duration-200 p-5 ${props.className || ""}`}
|
||||
onClick={props.onClick}
|
||||
className={`rounded-xl bg-white border border-gray-200 shadow-sm hover:shadow-md transition-shadow duration-200 p-5 ${props.onClick ? "cursor-pointer" : ""} ${props.className || ""}`}
|
||||
>
|
||||
<div className="mb-2">
|
||||
<FieldLabelElement title={props.title} />
|
||||
|
||||
@@ -73,11 +73,13 @@ data:
|
||||
mode: pull
|
||||
interval: {{ .Values.resourceSpecs.interval }}
|
||||
group: autoscaling
|
||||
{{- if .Values.resourceSpecs.vpa }}
|
||||
- name: verticalpodautoscalers
|
||||
mode: pull
|
||||
interval: {{ .Values.resourceSpecs.interval }}
|
||||
group: autoscaling.k8s.io
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- if or .Values.controlPlane.enabled .Values.serviceMesh.enabled }}
|
||||
# Scrape metrics via Prometheus endpoints (control plane and/or service mesh)
|
||||
|
||||
@@ -195,6 +195,10 @@
|
||||
"interval": {
|
||||
"type": "string",
|
||||
"description": "How often to pull resource specs (e.g., 300s, 5m)"
|
||||
},
|
||||
"vpa": {
|
||||
"type": "boolean",
|
||||
"description": "Enable VPA collection (requires VPA CRDs installed in the cluster)"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -76,6 +76,7 @@ logs:
|
||||
resourceSpecs:
|
||||
enabled: true
|
||||
interval: 300s # How often to pull full resource specs (default: 5 minutes)
|
||||
vpa: false # Enable VPA collection (requires VPA CRDs installed in the cluster)
|
||||
|
||||
# Collection intervals
|
||||
collectionInterval: 30s
|
||||
|
||||
@@ -278,9 +278,10 @@ export default class OtelLogsIngestService extends OtelIngestBaseService {
|
||||
if (
|
||||
logBody &&
|
||||
typeof logBody === "object" &&
|
||||
logBody["stringValue"]
|
||||
(logBody["stringValue"] || logBody["string_value"])
|
||||
) {
|
||||
body = logBody["stringValue"] as string;
|
||||
body = (logBody["stringValue"] ||
|
||||
logBody["string_value"]) as string;
|
||||
} else if (typeof log["body"] === "string") {
|
||||
body = log["body"] as string;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user