diff --git a/web/src/components/forms/resource-status.tsx b/web/src/components/forms/resource-status.tsx
index 877b14fb8..a189c81c7 100644
--- a/web/src/components/forms/resource-status.tsx
+++ b/web/src/components/forms/resource-status.tsx
@@ -15,6 +15,29 @@ import { useTranslation } from 'react-i18next';
import { useNavigate } from '../../utils/url';
import { ComponentStatus, ExporterStatus } from './pipeline';
+/** `FlowCollector.status` fields used by this form (mirrors operator CRD shape). */
+export type FlowCollectorStatus = {
+ conditions?: K8sResourceCondition[];
+ components?: {
+ agent?: ComponentStatus;
+ processor?: ComponentStatus;
+ plugin?: ComponentStatus;
+ };
+ integrations?: {
+ loki?: ComponentStatus;
+ monitoring?: ComponentStatus;
+ exporters?: ExporterStatus[];
+ };
+};
+
+function flowCollectorStatus(existing: K8sResourceKind | null): FlowCollectorStatus | undefined {
+ const raw = existing?.status;
+ if (raw == null || typeof raw !== 'object') {
+ return undefined;
+ }
+ return raw as FlowCollectorStatus;
+}
+
export type ResourceStatusProps = {
group: string;
version: string;
@@ -46,17 +69,17 @@ const stateColor = (state: string | undefined): LabelColor => {
const StateIcon: FC<{ state: string | undefined }> = ({ state }) => {
switch (state) {
case 'Ready':
- return ;
+ return ;
case 'Degraded':
- return ;
+ return ;
case 'Failure':
- return ;
+ return ;
case 'InProgress':
- return ;
+ return ;
case 'Unused':
- return ;
+ return ;
default:
- return ;
+ return ;
}
};
@@ -135,7 +158,7 @@ const ComponentStatusTable: FC<{
))}
{unusedComponents.length > 0 && (
- |
+ |
{t('Unused: {{list}}', { list: unusedComponents.map(c => c.name).join(', ') })}
|
@@ -146,6 +169,68 @@ const ComponentStatusTable: FC<{
);
};
+/**
+ * `Waiting*` FlowCollector conditions use inverted polarity (operator `statuses.go`): `True` means not ready.
+ * Component state on the same status object is the source of truth for Failure / Degraded / InProgress.
+ */
+const WAITING_NO_STATUS_FIELD = new Set(['FlowCollectorController', 'StaticController', 'NetworkPolicy']);
+
+type ConditionTone = 'error' | 'warning' | 'progress' | 'success' | 'unused' | 'unknown';
+
+function waitingComponentState(st: FlowCollectorStatus | undefined, suffix: string): string | undefined {
+ if (!st) return undefined;
+ const { components, integrations } = st;
+ switch (suffix) {
+ case 'EBPFAgents':
+ return components?.agent?.state;
+ case 'WebConsole':
+ return components?.plugin?.state;
+ case 'FLPMonolith':
+ case 'FLPParent':
+ case 'FLPTransformer':
+ return components?.processor?.state;
+ case 'Monitoring':
+ return integrations?.monitoring?.state;
+ case 'LokiStack':
+ case 'DemoLoki':
+ return integrations?.loki?.state;
+ default:
+ return undefined;
+ }
+}
+
+/** One tone per row: drives icon and message color. */
+function conditionTone(c: K8sResourceCondition, fcStatus: FlowCollectorStatus | undefined): ConditionTone {
+ const { type, status, reason } = c;
+
+ if (type === 'ConfigurationIssue') {
+ if (status === 'True' && reason === 'Error') return 'error';
+ if (status === 'True' && reason === 'Warnings') return 'warning';
+ return 'unknown';
+ }
+
+ if (type.startsWith('Waiting')) {
+ const suffix = type.slice('Waiting'.length);
+ if (status === 'False' && reason === 'Ready') return 'success';
+ // Operator `setUnused` sets reason `ComponentUnused`; `toCondition` default is `Unused`.
+ if (status === 'Unknown' && (reason === 'Unused' || reason === 'ComponentUnused')) return 'unused';
+ if (status !== 'True') return 'unknown';
+
+ const st = waitingComponentState(fcStatus, suffix);
+ if (st === 'Failure') return 'error';
+ if (st === 'Degraded') return 'warning';
+ if (st === 'InProgress') return 'progress';
+ if (st === 'Ready') return 'success';
+ return WAITING_NO_STATUS_FIELD.has(suffix) ? 'error' : 'progress';
+ }
+
+ if (type === 'Ready' && status === 'True' && reason === 'Ready,Degraded') return 'warning';
+ if (status === 'True') return 'success';
+ if (status === 'False' && reason === 'Pending') return 'progress';
+ if (status === 'False' && reason !== 'Valid') return 'error';
+ return 'unknown';
+}
+
export const ResourceStatus: FC = ({
group,
version,
@@ -172,30 +257,32 @@ export const ResourceStatus: FC = ({
);
}
+ const fcStatus = flowCollectorStatus(existing);
+
const components: ComponentRowData[] = [];
- if (existing.status?.components?.agent) {
- components.push({ id: 'agent', name: t('eBPF Agent'), status: existing.status.components.agent });
+ if (fcStatus?.components?.agent) {
+ components.push({ id: 'agent', name: t('eBPF Agent'), status: fcStatus.components.agent });
}
- if (existing.status?.components?.processor) {
- components.push({ id: 'processor', name: t('Flowlogs Pipeline'), status: existing.status.components.processor });
+ if (fcStatus?.components?.processor) {
+ components.push({ id: 'processor', name: t('Flowlogs Pipeline'), status: fcStatus.components.processor });
}
- if (existing.status?.components?.plugin) {
- components.push({ id: 'plugin', name: t('Console Plugin'), status: existing.status.components.plugin });
+ if (fcStatus?.components?.plugin) {
+ components.push({ id: 'plugin', name: t('Console Plugin'), status: fcStatus.components.plugin });
}
- if (existing.status?.integrations?.loki) {
- components.push({ id: 'loki', name: 'Loki', status: existing.status.integrations.loki });
+ if (fcStatus?.integrations?.loki) {
+ components.push({ id: 'loki', name: 'Loki', status: fcStatus.integrations.loki });
}
- if (existing.status?.integrations?.monitoring) {
- components.push({ id: 'monitoring', name: t('Monitoring'), status: existing.status.integrations.monitoring });
+ if (fcStatus?.integrations?.monitoring) {
+ components.push({ id: 'monitoring', name: t('Monitoring'), status: fcStatus.integrations.monitoring });
}
- const exporters: ExporterStatus[] = existing.status?.integrations?.exporters || [];
+ const exporters: ExporterStatus[] = fcStatus?.integrations?.exporters || [];
const sortConditions = [
(c: K8sResourceCondition) => c.type === 'Ready',
(c: K8sResourceCondition) => c.type === 'ConfigurationIssue',
(c: K8sResourceCondition) => c.type === 'KafkaReady'
];
- const conditions = ((existing?.status?.conditions || []) as K8sResourceCondition[]).sort((a, b) => {
+ const conditions = (fcStatus?.conditions || []).sort((a, b) => {
for (const pred of sortConditions) {
if (pred(a) && pred(b)) {
return 0;
@@ -232,16 +319,7 @@ export const ResourceStatus: FC = ({
{conditions.map((condition, i) => {
- const isWarning =
- condition.type === 'ConfigurationIssue' && condition.status === 'True' && condition.reason === 'Warnings';
- const isError =
- (condition.type === 'ConfigurationIssue' &&
- condition.status === 'True' &&
- condition.reason === 'Error') ||
- (condition.type !== 'ConfigurationIssue' &&
- condition.status === 'False' &&
- condition.reason !== 'Pending' &&
- condition.reason !== 'Valid');
+ const tone = conditionTone(condition, fcStatus);
return (
= ({
key={i}
>
|
- {isError ? (
-
- ) : isWarning ? (
-
- ) : condition.status === 'True' && condition.type !== 'ConfigurationIssue' ? (
-
- ) : condition.status === 'False' && condition.reason === 'Pending' ? (
-
+ {tone === 'error' ? (
+
+ ) : tone === 'warning' ? (
+
+ ) : tone === 'progress' ? (
+
+ ) : tone === 'unused' ? (
+
+ ) : tone === 'success' ? (
+
) : (
-
+
)}{' '}
{condition.type}
|
@@ -267,10 +347,10 @@ export const ResourceStatus: FC = ({
{condition.reason} |
diff --git a/web/src/components/status/flowcollector-status-icon.tsx b/web/src/components/status/flowcollector-status-icon.tsx
index d337cc679..3147b6bca 100644
--- a/web/src/components/status/flowcollector-status-icon.tsx
+++ b/web/src/components/status/flowcollector-status-icon.tsx
@@ -36,22 +36,22 @@ export const FlowCollectorStatusIcon: React.FC = (
const icon = React.useMemo(() => {
switch (status) {
case 'ready':
- return ;
+ return ;
case 'degraded':
- return ;
+ return ;
case 'pending':
- return ;
+ return ;
case 'error':
- return ;
+ return ;
case 'onHold':
- return ;
+ return ;
case 'loading':
return ;
}
}, [status]);
return (
-
+
{icon}
);
|