From f7c5c3e79f300f47c2cc3619bc1d127423a560ff Mon Sep 17 00:00:00 2001 From: Jason Madigan Date: Tue, 22 Oct 2024 12:36:38 +0100 Subject: [PATCH 1/2] GW status: enforced if affected by any policies succesfully Signed-off-by: Jason Madigan --- src/components/ResourceList.tsx | 190 +++++++++++++++++--------------- 1 file changed, 99 insertions(+), 91 deletions(-) diff --git a/src/components/ResourceList.tsx b/src/components/ResourceList.tsx index 5a9c3f1..b483222 100644 --- a/src/components/ResourceList.tsx +++ b/src/components/ResourceList.tsx @@ -35,131 +35,139 @@ import { } from '@patternfly/react-icons'; import DropdownWithKebab from './DropdownWithKebab'; -const getStatusLabel = (obj: any) => { - // Check if status has parents and handle accordingly - const parentConditions = obj.status?.parents?.flatMap((parent: any) => parent.conditions) || []; +const getStatusLabel = (obj) => { + const { kind, status } = obj; - if (parentConditions.length > 0) { - const acceptedCondition = parentConditions.find( - (cond: any) => cond.type === 'Accepted' && cond.status === 'True', + const policiesMap = { + Gateway: [ + 'kuadrant.io/DNSPolicyAffected', + 'kuadrant.io/TLSPolicyAffected', + 'kuadrant.io/AuthPolicyAffected', + 'kuadrant.io/RateLimitPolicyAffected', + ], + HTTPRoute: ['kuadrant.io/AuthPolicyAffected', 'kuadrant.io/RateLimitPolicyAffected'], + }; + + const policiesAffected = policiesMap[kind] || []; + + const hasAllPoliciesEnforced = (conditions) => { + return policiesAffected.every((policy) => + conditions.some((cond) => cond.type === policy && cond.status === 'True'), + ); + }; + + const hasAnyPolicyError = (conditions) => { + return policiesAffected.some((policy) => + conditions.some((cond) => cond.type === policy && cond.status === 'False'), + ); + }; + + const generateLabel = (labelText, color, icon) => ( + + ); + + if (kind === 'Gateway') { + const conditions = status?.conditions || []; + + const acceptedCondition = conditions.find( + (cond) => cond.type === 'Accepted' && cond.status === 'True', + ); + const programmedCondition = conditions.find( + (cond) => cond.type === 'Programmed' && cond.status === 'True', ); - const programmedCondition = parentConditions.find( - (cond: any) => cond.type === 'Programmed' && cond.status === 'True', + + if (acceptedCondition && programmedCondition) { + if (hasAllPoliciesEnforced(conditions) && !hasAnyPolicyError(conditions)) { + return generateLabel('Enforced', 'green', ); + } else { + return generateLabel('Accepted (Not Enforced)', 'purple', ); + } + } else if (programmedCondition) { + return generateLabel('Programmed', 'blue', ); + } else if (conditions.some((cond) => cond.type === 'Conflicted' && cond.status === 'True')) { + return generateLabel('Conflicted', 'red', ); + } else if (conditions.some((cond) => cond.type === 'ResolvedRefs' && cond.status === 'True')) { + return generateLabel('Resolved Refs', 'blue', ); + } else { + return generateLabel('Unknown', 'orange', ); + } + } + + if (policiesAffected.length > 0) { + const parentConditions = status?.parents?.flatMap((parent) => parent.conditions) || []; + + const acceptedCondition = parentConditions.find( + (cond) => cond.type === 'Accepted' && cond.status === 'True', ); const conflictedCondition = parentConditions.find( - (cond: any) => cond.type === 'Conflicted' && cond.status === 'True', + (cond) => cond.type === 'Conflicted' && cond.status === 'True', ); const resolvedRefsCondition = parentConditions.find( - (cond: any) => cond.type === 'ResolvedRefs' && cond.status === 'True', + (cond) => cond.type === 'ResolvedRefs' && cond.status === 'True', ); - let labelText: string; - let color: 'green' | 'blue' | 'red' | 'orange'; - let icon: React.ReactNode; - if (acceptedCondition) { - labelText = 'Accepted'; - color = 'green'; - icon = ; - } else if (programmedCondition) { - labelText = 'Programmed'; - color = 'blue'; - icon = ; + if (hasAllPoliciesEnforced(parentConditions) && !hasAnyPolicyError(parentConditions)) { + return generateLabel('Enforced', 'green', ); + } else { + return generateLabel('Accepted (Not Enforced)', 'purple', ); + } } else if (conflictedCondition) { - labelText = 'Conflicted'; - color = 'red'; - icon = ; + return generateLabel('Conflicted', 'red', ); } else if (resolvedRefsCondition) { - labelText = 'Resolved Refs'; - color = 'blue'; - icon = ; + return generateLabel('Resolved Refs', 'blue', ); } else { - labelText = 'Unknown'; - color = 'orange'; - icon = ; + return generateLabel('Unknown', 'orange', ); } - - return ( - - ); } - // If no parent conditions, fallback to general condition checks - if (!obj.status || !obj.status.conditions || obj.status.conditions.length === 0) { - return ( - - ); + const generalConditions = status?.conditions || []; + + if (generalConditions.length === 0) { + return generateLabel('Creating', 'cyan', ); } - const enforcedCondition = obj.status.conditions.find( - (cond: any) => cond.type === 'Enforced' && cond.status === 'True', + const enforcedCondition = generalConditions.find( + (cond) => cond.type === 'Enforced' && cond.status === 'True', ); - const acceptedCondition = obj.status.conditions.find( - (cond: any) => cond.type === 'Accepted' && cond.status === 'True', + const acceptedCondition = generalConditions.find( + (cond) => cond.type === 'Accepted' && cond.status === 'True', ); - const acceptedConditionFalse = obj.status.conditions.find( - (cond: any) => cond.type === 'Accepted' && cond.status === 'False', + const acceptedConditionFalse = generalConditions.find( + (cond) => cond.type === 'Accepted' && cond.status === 'False', ); - const overriddenCondition = obj.status.conditions.find( - (cond: any) => cond.type === 'Overridden' && cond.status === 'False', + const overriddenCondition = generalConditions.find( + (cond) => cond.type === 'Overridden' && cond.status === 'False', ); - const conflictedCondition = obj.status.conditions.find( - (cond: any) => cond.reason === 'Conflicted' && cond.status === 'False', + const conflictedCondition = generalConditions.find( + (cond) => cond.reason === 'Conflicted' && cond.status === 'False', ); - const targetNotFoundCondition = obj.status.conditions.find( - (cond: any) => cond.reason === 'TargetNotFound' && cond.status === 'False', + const targetNotFoundCondition = generalConditions.find( + (cond) => cond.reason === 'TargetNotFound' && cond.status === 'False', ); - const unknownCondition = obj.status.conditions.find( - (cond: any) => cond.reason === 'Unknown' && cond.status === 'False', + const unknownCondition = generalConditions.find( + (cond) => cond.reason === 'Unknown' && cond.status === 'False', ); - let labelText: string; - let color: 'blue' | 'green' | 'red' | 'orange' | 'grey' | 'purple' | 'cyan'; - let icon: React.ReactNode; - if (enforcedCondition) { - labelText = 'Enforced'; - color = 'green'; - icon = ; + return generateLabel('Enforced', 'green', ); } else if (overriddenCondition) { - labelText = 'Overridden (Not Enforced)'; - color = 'grey'; - icon = ; + return generateLabel('Overridden (Not Enforced)', 'grey', ); } else if (acceptedCondition) { - labelText = 'Accepted (Not Enforced)'; - color = 'purple'; - icon = ; + return generateLabel('Accepted (Not Enforced)', 'purple', ); } else if (conflictedCondition) { - labelText = 'Conflicted (Not Accepted)'; - color = 'red'; - icon = ; + return generateLabel('Conflicted (Not Accepted)', 'red', ); } else if (targetNotFoundCondition) { - labelText = 'TargetNotFound (Not Accepted)'; - color = 'red'; - icon = ; + return generateLabel('TargetNotFound (Not Accepted)', 'red', ); } else if (unknownCondition) { - labelText = 'Unknown (Not Accepted)'; - color = 'orange'; - icon = ; + return generateLabel('Unknown (Not Accepted)', 'orange', ); } else if (acceptedConditionFalse) { - labelText = 'Invalid (Not Accepted)'; - color = 'red'; - icon = ; + return generateLabel('Invalid (Not Accepted)', 'red', ); } else { - labelText = 'Unknown'; - color = 'grey'; - icon = ; + return generateLabel('Unknown', 'grey', ); } - - return ( - - ); }; type ResourceListProps = { From 26175379da7901ca4acb85aa2c89782cb79de389 Mon Sep 17 00:00:00 2001 From: Jason Madigan Date: Wed, 23 Oct 2024 15:57:19 +0100 Subject: [PATCH 2/2] adding tooltips to status labels Signed-off-by: Jason Madigan --- .../en/plugin__kuadrant-console-plugin.json | 12 ++ src/components/ResourceList.tsx | 176 +++++++++++++++--- 2 files changed, 162 insertions(+), 26 deletions(-) diff --git a/locales/en/plugin__kuadrant-console-plugin.json b/locales/en/plugin__kuadrant-console-plugin.json index 459ecc9..9aa8813 100644 --- a/locales/en/plugin__kuadrant-console-plugin.json +++ b/locales/en/plugin__kuadrant-console-plugin.json @@ -3,6 +3,7 @@ "Add a new Gateway": "Add a new Gateway", "Add Limit": "Add Limit", "All Policies": "All Policies", + "All references for the resource have been resolved.": "All references for the resource have been resolved.", "API Designer": "API Designer", "APIs / HTTPRoutes": "APIs / HTTPRoutes", "Auth": "Auth", @@ -96,7 +97,18 @@ "Select Issuer": "Select Issuer", "Status": "Status", "Targets Gateway API networking resources Gateways to provide TLS for gateway listeners by managing the lifecycle of TLS certificates using cert-manager": "Targets Gateway API networking resources Gateways to provide TLS for gateway listeners by managing the lifecycle of TLS certificates using cert-manager", + "The resource has an unknown status and is not accepted.": "The resource has an unknown status and is not accepted.", + "The resource is accepted but not all policies are enforced.": "The resource is accepted but not all policies are enforced.", + "The resource is accepted, programmed, and all policies are enforced.": "The resource is accepted, programmed, and all policies are enforced.", + "The resource is being created.": "The resource is being created.", + "The resource is invalid and not accepted.": "The resource is invalid and not accepted.", + "The resource is overridden and not enforced.": "The resource is overridden and not enforced.", + "The resource is programmed but not fully enforced.": "The resource is programmed but not fully enforced.", + "The status of the resource is unknown.": "The status of the resource is unknown.", + "The target for the resource was not found and it is not accepted.": "The target for the resource was not found and it is not accepted.", "There are no": "There are no", + "There is a conflict and the resource is not accepted.": "There is a conflict and the resource is not accepted.", + "There is a conflict on the resource.": "There is a conflict on the resource.", "TLS": "TLS", "TLSPolicy": "TLSPolicy", "to display - please create some.": "to display - please create some.", diff --git a/src/components/ResourceList.tsx b/src/components/ResourceList.tsx index b483222..df67828 100644 --- a/src/components/ResourceList.tsx +++ b/src/components/ResourceList.tsx @@ -10,6 +10,7 @@ import { EmptyStateIcon, EmptyStateBody, Title, + Tooltip, } from '@patternfly/react-core'; import { K8sResourceCommon, @@ -35,7 +36,36 @@ import { } from '@patternfly/react-icons'; import DropdownWithKebab from './DropdownWithKebab'; +const generateLabelWithTooltip = (labelText, color, icon, tooltipText) => { + return ( + + + + ); +}; + const getStatusLabel = (obj) => { + const { t } = useTranslation('plugin__kuadrant-console-plugin'); + + const tooltipTexts = { + Enforced: t('The resource is accepted, programmed, and all policies are enforced.'), + 'Accepted (Not Enforced)': t('The resource is accepted but not all policies are enforced.'), + Programmed: t('The resource is programmed but not fully enforced.'), + Conflicted: t('There is a conflict on the resource.'), + 'Resolved Refs': t('All references for the resource have been resolved.'), + Unknown: t('The status of the resource is unknown.'), + Creating: t('The resource is being created.'), + 'Overridden (Not Enforced)': t('The resource is overridden and not enforced.'), + 'Conflicted (Not Accepted)': t('There is a conflict and the resource is not accepted.'), + 'TargetNotFound (Not Accepted)': t( + 'The target for the resource was not found and it is not accepted.', + ), + 'Unknown (Not Accepted)': t('The resource has an unknown status and is not accepted.'), + 'Invalid (Not Accepted)': t('The resource is invalid and not accepted.'), + }; + const { kind, status } = obj; const policiesMap = { @@ -62,12 +92,6 @@ const getStatusLabel = (obj) => { ); }; - const generateLabel = (labelText, color, icon) => ( - - ); - if (kind === 'Gateway') { const conditions = status?.conditions || []; @@ -80,18 +104,48 @@ const getStatusLabel = (obj) => { if (acceptedCondition && programmedCondition) { if (hasAllPoliciesEnforced(conditions) && !hasAnyPolicyError(conditions)) { - return generateLabel('Enforced', 'green', ); + return generateLabelWithTooltip( + 'Enforced', + 'green', + , + tooltipTexts['Enforced'], + ); } else { - return generateLabel('Accepted (Not Enforced)', 'purple', ); + return generateLabelWithTooltip( + 'Accepted (Not Enforced)', + 'purple', + , + tooltipTexts['Accepted (Not Enforced)'], + ); } } else if (programmedCondition) { - return generateLabel('Programmed', 'blue', ); + return generateLabelWithTooltip( + 'Programmed', + 'blue', + , + tooltipTexts['Programmed'], + ); } else if (conditions.some((cond) => cond.type === 'Conflicted' && cond.status === 'True')) { - return generateLabel('Conflicted', 'red', ); + return generateLabelWithTooltip( + 'Conflicted', + 'red', + , + tooltipTexts['Conflicted'], + ); } else if (conditions.some((cond) => cond.type === 'ResolvedRefs' && cond.status === 'True')) { - return generateLabel('Resolved Refs', 'blue', ); + return generateLabelWithTooltip( + 'Resolved Refs', + 'blue', + , + tooltipTexts['Resolved Refs'], + ); } else { - return generateLabel('Unknown', 'orange', ); + return generateLabelWithTooltip( + 'Unknown', + 'orange', + , + tooltipTexts['Unknown'], + ); } } @@ -110,23 +164,53 @@ const getStatusLabel = (obj) => { if (acceptedCondition) { if (hasAllPoliciesEnforced(parentConditions) && !hasAnyPolicyError(parentConditions)) { - return generateLabel('Enforced', 'green', ); + return generateLabelWithTooltip( + 'Enforced', + 'green', + , + tooltipTexts['Enforced'], + ); } else { - return generateLabel('Accepted (Not Enforced)', 'purple', ); + return generateLabelWithTooltip( + 'Accepted (Not Enforced)', + 'purple', + , + tooltipTexts['Accepted (Not Enforced)'], + ); } } else if (conflictedCondition) { - return generateLabel('Conflicted', 'red', ); + return generateLabelWithTooltip( + 'Conflicted', + 'red', + , + tooltipTexts['Conflicted'], + ); } else if (resolvedRefsCondition) { - return generateLabel('Resolved Refs', 'blue', ); + return generateLabelWithTooltip( + 'Resolved Refs', + 'blue', + , + tooltipTexts['Resolved Refs'], + ); } else { - return generateLabel('Unknown', 'orange', ); + return generateLabelWithTooltip( + 'Unknown', + 'orange', + , + tooltipTexts['Unknown'], + ); } } const generalConditions = status?.conditions || []; if (generalConditions.length === 0) { - return generateLabel('Creating', 'cyan', ); + return generateLabelWithTooltip( + 'Creating', + 'cyan', + , + tooltipTexts['Creating'], + ); } const enforcedCondition = generalConditions.find( @@ -152,21 +236,61 @@ const getStatusLabel = (obj) => { ); if (enforcedCondition) { - return generateLabel('Enforced', 'green', ); + return generateLabelWithTooltip( + 'Enforced', + 'green', + , + tooltipTexts['Enforced'], + ); } else if (overriddenCondition) { - return generateLabel('Overridden (Not Enforced)', 'grey', ); + return generateLabelWithTooltip( + 'Overridden (Not Enforced)', + 'grey', + , + tooltipTexts['Overridden (Not Enforced)'], + ); } else if (acceptedCondition) { - return generateLabel('Accepted (Not Enforced)', 'purple', ); + return generateLabelWithTooltip( + 'Accepted (Not Enforced)', + 'purple', + , + tooltipTexts['Accepted (Not Enforced)'], + ); } else if (conflictedCondition) { - return generateLabel('Conflicted (Not Accepted)', 'red', ); + return generateLabelWithTooltip( + 'Conflicted (Not Accepted)', + 'red', + , + tooltipTexts['Conflicted (Not Accepted)'], + ); } else if (targetNotFoundCondition) { - return generateLabel('TargetNotFound (Not Accepted)', 'red', ); + return generateLabelWithTooltip( + 'TargetNotFound (Not Accepted)', + 'red', + , + tooltipTexts['TargetNotFound (Not Accepted)'], + ); } else if (unknownCondition) { - return generateLabel('Unknown (Not Accepted)', 'orange', ); + return generateLabelWithTooltip( + 'Unknown (Not Accepted)', + 'orange', + , + tooltipTexts['Unknown (Not Accepted)'], + ); } else if (acceptedConditionFalse) { - return generateLabel('Invalid (Not Accepted)', 'red', ); + return generateLabelWithTooltip( + 'Invalid (Not Accepted)', + 'red', + , + tooltipTexts['Invalid (Not Accepted)'], + ); } else { - return generateLabel('Unknown', 'grey', ); + return generateLabelWithTooltip( + 'Unknown', + 'grey', + , + tooltipTexts['Unknown'], + ); } };