@skyhook-io/k8s-ui 1.8.9 → 1.8.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -4
- package/src/assets/gitops/argocd.png +0 -0
- package/src/assets/gitops/flux.svg +7 -0
- package/src/components/applications/AppChips.tsx +10 -0
- package/src/components/applications/ApplicationDetail.test.tsx +569 -0
- package/src/components/applications/ApplicationDetail.tsx +2517 -450
- package/src/components/applications/ApplicationsList.tsx +4 -1
- package/src/components/applications/ApplicationsView.tsx +101 -18
- package/src/components/applications/applications-view.test.tsx +47 -0
- package/src/components/applications/index.ts +1 -1
- package/src/components/audit/AuditAlerts.tsx +14 -10
- package/src/components/checks/ChecksView.tsx +81 -51
- package/src/components/checks/severity.ts +39 -45
- package/src/components/cluster-switcher/ClusterSwitcher.tsx +2 -2
- package/src/components/compare/CompareResourcePicker.tsx +2 -2
- package/src/components/gitops/GitOpsGraphFilterRail.tsx +2 -1
- package/src/components/gitops/GitOpsTableView.tsx +14 -59
- package/src/components/gitops/ManagedResourcesList.tsx +8 -11
- package/src/components/gitops/SyncCountdown.tsx +26 -17
- package/src/components/gitops/SyncOptionsDialog.tsx +2 -2
- package/src/components/gitops/insights/GitOpsInsightViews.tsx +277 -104
- package/src/components/gitops/insights/insights-helpers.test.ts +86 -1
- package/src/components/gitops/insights/insights-helpers.ts +69 -1
- package/src/components/gitops/tree/GitOpsTreeGraph.tsx +5 -2
- package/src/components/issues/IssuesView.tsx +219 -143
- package/src/components/issues/ResourceIssuesSection.test.tsx +40 -0
- package/src/components/issues/ResourceIssuesSection.tsx +28 -128
- package/src/components/issues/index.ts +3 -2
- package/src/components/issues/issue-timing.ts +53 -0
- package/src/components/issues/issues.test.ts +146 -4
- package/src/components/issues/severity.ts +29 -32
- package/src/components/logs/LogCore.tsx +14 -4
- package/src/components/logs/WorkloadLogsViewer.tsx +31 -6
- package/src/components/logs/useLogStream.ts +8 -1
- package/src/components/namespace-switcher/NamespacePicker.tsx +86 -107
- package/src/components/resources/ResourcesSidebar.test.tsx +107 -0
- package/src/components/resources/ResourcesSidebar.tsx +70 -52
- package/src/components/resources/ResourcesView.selected-kind.test.ts +143 -0
- package/src/components/resources/ResourcesView.tsx +243 -53
- package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +2 -2
- package/src/components/resources/renderers/CronWorkflowRenderer.tsx +99 -0
- package/src/components/resources/renderers/JobRenderer.tsx +3 -1
- package/src/components/resources/renderers/MetricsUnavailableNotice.tsx +47 -0
- package/src/components/resources/renderers/NodeRenderer.test.tsx +103 -0
- package/src/components/resources/renderers/NodeRenderer.tsx +20 -12
- package/src/components/resources/renderers/PVCRenderer.test.tsx +23 -0
- package/src/components/resources/renderers/PVCRenderer.tsx +2 -2
- package/src/components/resources/renderers/PodRenderer.test.tsx +101 -0
- package/src/components/resources/renderers/PodRenderer.tsx +81 -70
- package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +2 -2
- package/src/components/resources/renderers/SbomReportRenderer.tsx +2 -2
- package/src/components/resources/renderers/SecretRenderer.tsx +2 -0
- package/src/components/resources/renderers/ServiceRenderer.tsx +58 -40
- package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +2 -2
- package/src/components/resources/renderers/WorkflowRenderer.test.tsx +43 -0
- package/src/components/resources/renderers/WorkflowRenderer.tsx +60 -78
- package/src/components/resources/renderers/index.ts +1 -0
- package/src/components/resources/resource-utils-capi.ts +1 -1
- package/src/components/resources/resource-utils.ts +31 -0
- package/src/components/shared/DetailShell.tsx +2 -2
- package/src/components/shared/ResourceActionsBar.tsx +140 -108
- package/src/components/shared/ResourceRendererDispatch.tsx +33 -4
- package/src/components/timeline/TimelineList.tsx +371 -354
- package/src/components/timeline/TimelineStrip.test.tsx +194 -0
- package/src/components/timeline/TimelineStrip.tsx +836 -0
- package/src/components/timeline/TimelineSwimlanes.test.tsx +1028 -0
- package/src/components/timeline/TimelineSwimlanes.tsx +2498 -593
- package/src/components/timeline/TimelineToolbar.test.tsx +320 -0
- package/src/components/timeline/TimelineToolbar.tsx +759 -0
- package/src/components/timeline/TimelineToolbarState.test.tsx +64 -0
- package/src/components/timeline/index.ts +23 -0
- package/src/components/timeline/scrubber-math.test.ts +235 -0
- package/src/components/timeline/scrubber-math.ts +249 -0
- package/src/components/timeline/shared.test.tsx +219 -0
- package/src/components/timeline/shared.tsx +232 -447
- package/src/components/timeline/timeline-filters.test.ts +196 -0
- package/src/components/timeline/timeline-filters.ts +180 -0
- package/src/components/timeline/timeline-lane-sort.test.ts +97 -0
- package/src/components/timeline/timeline-lane-sort.ts +77 -0
- package/src/components/timeline/timeline-live.test.ts +131 -0
- package/src/components/timeline/timeline-live.ts +77 -0
- package/src/components/topology/GroupNode.tsx +9 -2
- package/src/components/topology/K8sResourceNode.test.ts +11 -0
- package/src/components/topology/K8sResourceNode.tsx +387 -243
- package/src/components/topology/TopologyFilterSidebar.tsx +9 -0
- package/src/components/topology/TopologyGraph.tsx +89 -16
- package/src/components/topology/TopologySearch.tsx +2 -2
- package/src/components/topology/layout.ts +10 -2
- package/src/components/topology/topology.css +0 -92
- package/src/components/ui/Badge.tsx +5 -0
- package/src/components/ui/BoardSkeleton.tsx +47 -0
- package/src/components/ui/CardSection.tsx +117 -0
- package/src/components/ui/CodeViewer.tsx +2 -2
- package/src/components/ui/Collapse.tsx +59 -0
- package/src/components/ui/ConfirmDialog.tsx +7 -3
- package/src/components/ui/DialogPortal.tsx +31 -9
- package/src/components/ui/Input.tsx +24 -0
- package/src/components/ui/MultiSelectPicker.test.tsx +101 -0
- package/src/components/ui/MultiSelectPicker.tsx +167 -0
- package/src/components/ui/PaneLoader.tsx +10 -2
- package/src/components/ui/ResourceBar.tsx +68 -25
- package/src/components/ui/SearchBox.tsx +18 -4
- package/src/components/ui/SearchPillInput.tsx +2 -2
- package/src/components/ui/Toast.tsx +1 -1
- package/src/components/ui/drawer-components.tsx +39 -26
- package/src/components/ui/index.ts +6 -0
- package/src/components/ui/severity-tone.ts +63 -0
- package/src/components/workload/ResourceDetailDrawer.tsx +6 -2
- package/src/components/workload/WorkloadView.tsx +2682 -472
- package/src/components/workload/index.ts +1 -1
- package/src/theme/variables.css +15 -1
- package/src/types/core.ts +57 -4
- package/src/utils/api-resources.test.ts +54 -0
- package/src/utils/api-resources.ts +68 -1
- package/src/utils/application-topology.test.ts +198 -0
- package/src/utils/application-topology.ts +261 -0
- package/src/utils/applications.test.ts +167 -1
- package/src/utils/applications.ts +1157 -393
- package/src/utils/index.ts +2 -0
- package/src/utils/navigation.test.ts +32 -1
- package/src/utils/navigation.ts +70 -0
- package/src/utils/resource-hierarchy.test.ts +990 -1
- package/src/utils/resource-hierarchy.ts +905 -267
- package/src/utils/resource-icons.ts +2 -0
- package/src/utils/topology-neighborhood.test.ts +202 -1
- package/src/utils/topology-neighborhood.ts +194 -13
- package/src/utils/workflow-execution.test.ts +556 -0
- package/src/utils/workflow-execution.ts +814 -0
- package/src/utils/workload-colors.ts +2 -3
- package/tsconfig.json +2 -2
|
@@ -150,6 +150,12 @@ export function ResourceActionsBar({
|
|
|
150
150
|
onDrainNode, isDrainingNode,
|
|
151
151
|
}: ResourceActionsBarProps) {
|
|
152
152
|
const kind = resource.kind.toLowerCase()
|
|
153
|
+
const canOpenWorkloadLogs = Boolean(
|
|
154
|
+
canViewLogs &&
|
|
155
|
+
!hideLogs &&
|
|
156
|
+
openWorkloadLogs &&
|
|
157
|
+
['deployments', 'statefulsets', 'daemonsets', 'jobs', 'workflows', 'cronjobs', 'cronworkflows', 'workflowtemplates', 'clusterworkflowtemplates', 'scaledjobs'].includes(kind)
|
|
158
|
+
)
|
|
153
159
|
|
|
154
160
|
// Delete confirmation state
|
|
155
161
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)
|
|
@@ -362,33 +368,21 @@ export function ResourceActionsBar({
|
|
|
362
368
|
</button>
|
|
363
369
|
)}
|
|
364
370
|
{isRollbackKind && onRollback && (
|
|
365
|
-
<
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
{canViewLogs && !hideLogs && ['deployments', 'statefulsets', 'daemonsets'].includes(kind) && openWorkloadLogs && (
|
|
381
|
-
<button
|
|
382
|
-
onClick={() => openWorkloadLogs({
|
|
383
|
-
namespace: resource.namespace,
|
|
384
|
-
workloadKind: kind,
|
|
385
|
-
workloadName: resource.name,
|
|
386
|
-
})}
|
|
387
|
-
className="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium btn-brand-muted rounded-lg"
|
|
388
|
-
>
|
|
389
|
-
<FileText className="w-3.5 h-3.5" />
|
|
390
|
-
Logs
|
|
391
|
-
</button>
|
|
371
|
+
<Tooltip content={hasMultipleRevisions ? 'View revision history and rollback' : 'Only one revision exists'} delay={150}>
|
|
372
|
+
<button
|
|
373
|
+
onClick={() => setShowRevisions(true)}
|
|
374
|
+
disabled={!hasMultipleRevisions}
|
|
375
|
+
className={clsx(
|
|
376
|
+
"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg transition-colors",
|
|
377
|
+
hasMultipleRevisions
|
|
378
|
+
? "text-white bg-amber-600 hover:bg-amber-700"
|
|
379
|
+
: "text-theme-text-disabled bg-theme-elevated"
|
|
380
|
+
)}
|
|
381
|
+
>
|
|
382
|
+
<History className="w-3.5 h-3.5" />
|
|
383
|
+
Rollback
|
|
384
|
+
</button>
|
|
385
|
+
</Tooltip>
|
|
392
386
|
)}
|
|
393
387
|
</>
|
|
394
388
|
)}
|
|
@@ -473,8 +467,22 @@ export function ResourceActionsBar({
|
|
|
473
467
|
/>
|
|
474
468
|
)}
|
|
475
469
|
|
|
470
|
+
{canOpenWorkloadLogs && (
|
|
471
|
+
<button
|
|
472
|
+
onClick={() => openWorkloadLogs?.({
|
|
473
|
+
namespace: resource.namespace,
|
|
474
|
+
workloadKind: kind,
|
|
475
|
+
workloadName: resource.name,
|
|
476
|
+
})}
|
|
477
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium btn-brand-muted rounded-lg"
|
|
478
|
+
>
|
|
479
|
+
<FileText className="w-3.5 h-3.5" />
|
|
480
|
+
Logs
|
|
481
|
+
</button>
|
|
482
|
+
)}
|
|
483
|
+
|
|
476
484
|
{/* Job logs */}
|
|
477
|
-
{kind === 'jobs' && onCopyCommand && (
|
|
485
|
+
{kind === 'jobs' && onCopyCommand && (!canViewLogs || !openWorkloadLogs) && (
|
|
478
486
|
<button
|
|
479
487
|
onClick={(e) => onCopyCommand(
|
|
480
488
|
`kubectl logs job/${resource.name} -n ${resource.namespace} -f`,
|
|
@@ -488,26 +496,43 @@ export function ResourceActionsBar({
|
|
|
488
496
|
</button>
|
|
489
497
|
)}
|
|
490
498
|
|
|
491
|
-
{
|
|
492
|
-
<div className="flex-1" />
|
|
493
|
-
|
|
494
|
-
{/* Universal actions (right-aligned) */}
|
|
495
|
-
{onToggleYaml && (
|
|
499
|
+
{kind === 'workflows' && onCopyCommand && (!canViewLogs || !openWorkloadLogs) && (
|
|
496
500
|
<button
|
|
497
|
-
onClick={
|
|
498
|
-
|
|
499
|
-
'
|
|
500
|
-
|
|
501
|
-
? 'btn-brand'
|
|
502
|
-
: 'text-theme-text-secondary hover:text-theme-text-primary border border-theme-border-light hover:bg-theme-elevated'
|
|
501
|
+
onClick={(e) => onCopyCommand(
|
|
502
|
+
`argo logs ${resource.name} -n ${resource.namespace}`,
|
|
503
|
+
'Logs command copied',
|
|
504
|
+
e
|
|
503
505
|
)}
|
|
504
|
-
|
|
506
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium btn-brand-muted rounded-lg"
|
|
505
507
|
>
|
|
506
|
-
<
|
|
507
|
-
|
|
508
|
+
<FileText className="w-3.5 h-3.5" />
|
|
509
|
+
Logs
|
|
508
510
|
</button>
|
|
509
511
|
)}
|
|
510
512
|
|
|
513
|
+
{/* Spacer pushes universal actions to the right */}
|
|
514
|
+
<div className="flex-1" />
|
|
515
|
+
|
|
516
|
+
{/* Universal actions (right-aligned). The AI/Diagnose action is NOT here — it
|
|
517
|
+
lives in the detail header chrome (see WorkloadView), set apart from these
|
|
518
|
+
imperative ops. */}
|
|
519
|
+
{onToggleYaml && (
|
|
520
|
+
<Tooltip content="Toggle YAML view" delay={150}>
|
|
521
|
+
<button
|
|
522
|
+
onClick={onToggleYaml}
|
|
523
|
+
className={clsx(
|
|
524
|
+
'flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg transition-colors',
|
|
525
|
+
showYaml
|
|
526
|
+
? 'btn-brand'
|
|
527
|
+
: 'text-theme-text-secondary hover:text-theme-text-primary border border-theme-border-light hover:bg-theme-elevated'
|
|
528
|
+
)}
|
|
529
|
+
>
|
|
530
|
+
<FileCode2 className="w-3.5 h-3.5" />
|
|
531
|
+
YAML
|
|
532
|
+
</button>
|
|
533
|
+
</Tooltip>
|
|
534
|
+
)}
|
|
535
|
+
|
|
511
536
|
{(onCompareTo || onCompareAcrossClusters) && (
|
|
512
537
|
<div
|
|
513
538
|
className="relative"
|
|
@@ -669,35 +694,37 @@ function FluxActions({ resource, data, onReconcile, isReconciling, onSyncWithSou
|
|
|
669
694
|
return (
|
|
670
695
|
<>
|
|
671
696
|
{onReconcile && (
|
|
672
|
-
<
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
697
|
+
<Tooltip content={isSuspended ? 'Cannot reconcile while suspended' : 'Trigger reconciliation'} delay={150}>
|
|
698
|
+
<button
|
|
699
|
+
onClick={() => onReconcile({
|
|
700
|
+
kind: resource.kind,
|
|
701
|
+
namespace: resource.namespace,
|
|
702
|
+
name: resource.name,
|
|
703
|
+
})}
|
|
704
|
+
disabled={isReconciling || isSuspended}
|
|
705
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium btn-brand rounded-lg"
|
|
706
|
+
>
|
|
707
|
+
<RefreshCw className={`w-3.5 h-3.5 ${isReconciling ? 'animate-spin' : ''}`} />
|
|
708
|
+
{isReconciling ? 'Reconciling...' : 'Reconcile'}
|
|
709
|
+
</button>
|
|
710
|
+
</Tooltip>
|
|
685
711
|
)}
|
|
686
712
|
|
|
687
713
|
{hasSource && onSyncWithSource && (
|
|
688
|
-
<
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
714
|
+
<Tooltip content={isSuspended ? 'Cannot sync while suspended' : 'Fetch latest from source, then reconcile'} delay={150}>
|
|
715
|
+
<button
|
|
716
|
+
onClick={() => onSyncWithSource({
|
|
717
|
+
kind: resource.kind,
|
|
718
|
+
namespace: resource.namespace,
|
|
719
|
+
name: resource.name,
|
|
720
|
+
})}
|
|
721
|
+
disabled={isSyncing || isSuspended}
|
|
722
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-white bg-purple-600 hover:bg-purple-700 rounded-lg transition-colors disabled:opacity-50"
|
|
723
|
+
>
|
|
724
|
+
<RefreshCw className={`w-3.5 h-3.5 ${isSyncing ? 'animate-spin' : ''}`} />
|
|
725
|
+
{isSyncing ? 'Syncing...' : 'Sync with Source'}
|
|
726
|
+
</button>
|
|
727
|
+
</Tooltip>
|
|
701
728
|
)}
|
|
702
729
|
|
|
703
730
|
{isSuspended ? (
|
|
@@ -751,34 +778,36 @@ function ArgoActions({ resource, data, onSync, isSyncing, onRefresh, isRefreshin
|
|
|
751
778
|
return (
|
|
752
779
|
<>
|
|
753
780
|
{onSync && (
|
|
754
|
-
<
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
781
|
+
<Tooltip content="Sync application" delay={150}>
|
|
782
|
+
<button
|
|
783
|
+
onClick={() => onSync({
|
|
784
|
+
namespace: resource.namespace,
|
|
785
|
+
name: resource.name,
|
|
786
|
+
})}
|
|
787
|
+
disabled={isSyncing}
|
|
788
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium btn-brand rounded-lg"
|
|
789
|
+
>
|
|
790
|
+
<RefreshCw className={`w-3.5 h-3.5 ${isSyncing ? 'animate-spin' : ''}`} />
|
|
791
|
+
{isSyncing ? 'Syncing...' : 'Sync'}
|
|
792
|
+
</button>
|
|
793
|
+
</Tooltip>
|
|
766
794
|
)}
|
|
767
795
|
|
|
768
796
|
{onRefresh && (
|
|
769
|
-
<
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
797
|
+
<Tooltip content="Refresh (re-read from git)" delay={150}>
|
|
798
|
+
<button
|
|
799
|
+
onClick={() => onRefresh({
|
|
800
|
+
namespace: resource.namespace,
|
|
801
|
+
name: resource.name,
|
|
802
|
+
hard: false,
|
|
803
|
+
})}
|
|
804
|
+
disabled={isRefreshing}
|
|
805
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium btn-brand-muted rounded-lg"
|
|
806
|
+
>
|
|
807
|
+
<RefreshCw className={`w-3.5 h-3.5 ${isRefreshing ? 'animate-spin' : ''}`} />
|
|
808
|
+
{isRefreshing ? 'Refreshing...' : 'Refresh'}
|
|
809
|
+
</button>
|
|
810
|
+
</Tooltip>
|
|
782
811
|
)}
|
|
783
812
|
|
|
784
813
|
{hasAutomatedSync ? (
|
|
@@ -945,8 +974,10 @@ export function RevisionHistoryDialog({ kind, namespace, name, open, onClose, re
|
|
|
945
974
|
<td className="py-2 pr-3 text-theme-text-primary font-mono">
|
|
946
975
|
#{rev.number}
|
|
947
976
|
</td>
|
|
948
|
-
<td className="py-2 pr-3 text-theme-text-secondary font-mono
|
|
949
|
-
{
|
|
977
|
+
<td className="py-2 pr-3 text-theme-text-secondary font-mono max-w-[180px]">
|
|
978
|
+
<Tooltip content={rev.image} delay={300} wrapperClassName="min-w-0">
|
|
979
|
+
<span className="truncate">{getImageTag(rev.image)}</span>
|
|
980
|
+
</Tooltip>
|
|
950
981
|
</td>
|
|
951
982
|
<td className="py-2 pr-3 text-theme-text-secondary whitespace-nowrap">
|
|
952
983
|
{formatTimeAgo(rev.createdAt)}
|
|
@@ -954,19 +985,20 @@ export function RevisionHistoryDialog({ kind, namespace, name, open, onClose, re
|
|
|
954
985
|
<td className="py-2 text-right">
|
|
955
986
|
<div className="flex items-center gap-1 justify-end">
|
|
956
987
|
{!rev.isCurrent && rev.template && currentRevision?.template && (
|
|
957
|
-
<
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
988
|
+
<Tooltip content="Compare with current revision" delay={150}>
|
|
989
|
+
<button
|
|
990
|
+
onClick={() => setDiffRevision(diffRevision === rev.number ? null : rev.number)}
|
|
991
|
+
className={clsx(
|
|
992
|
+
"px-2 py-0.5 text-xs font-medium rounded transition-colors flex items-center gap-1",
|
|
993
|
+
diffRevision === rev.number
|
|
994
|
+
? "bg-blue-500/20 text-blue-400 border border-blue-400/50"
|
|
995
|
+
: "text-blue-400 hover:text-blue-300 hover:bg-blue-500/10 border border-transparent"
|
|
996
|
+
)}
|
|
997
|
+
>
|
|
998
|
+
<GitCompare className="w-3 h-3" />
|
|
999
|
+
Diff
|
|
1000
|
+
</button>
|
|
1001
|
+
</Tooltip>
|
|
970
1002
|
)}
|
|
971
1003
|
{rev.isCurrent ? (
|
|
972
1004
|
<span className="badge status-healthy">
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getPVCStatus,
|
|
12
12
|
getRolloutStatus,
|
|
13
13
|
getWorkflowStatus,
|
|
14
|
+
getCronWorkflowStatus,
|
|
14
15
|
getCertificateStatus,
|
|
15
16
|
getPVStatus,
|
|
16
17
|
getClusterIssuerStatus,
|
|
@@ -82,6 +83,7 @@ import {
|
|
|
82
83
|
SecretRenderer,
|
|
83
84
|
JobRenderer,
|
|
84
85
|
CronJobRenderer,
|
|
86
|
+
CronWorkflowRenderer,
|
|
85
87
|
HPARenderer,
|
|
86
88
|
NodeRenderer,
|
|
87
89
|
PVCRenderer,
|
|
@@ -316,12 +318,12 @@ export interface RendererOverrides {
|
|
|
316
318
|
// Known resource types with specific renderers (module-level to avoid re-allocation)
|
|
317
319
|
const KNOWN_KINDS = new Set([
|
|
318
320
|
'pods', 'deployments', 'statefulsets', 'daemonsets', 'replicasets',
|
|
319
|
-
'services', 'endpointslices', 'ingresses', 'configmaps', 'secrets', 'jobs', 'cronjobs',
|
|
321
|
+
'services', 'endpointslices', 'ingresses', 'configmaps', 'secrets', 'jobs', 'cronjobs', 'cronworkflows',
|
|
320
322
|
'hpas', 'horizontalpodautoscalers', 'nodes', 'persistentvolumeclaims',
|
|
321
323
|
'rollouts', 'certificates', 'workflows', 'persistentvolumes',
|
|
322
324
|
'storageclasses', 'certificaterequests', 'clusterissuers', 'issuers',
|
|
323
325
|
'orders', 'challenges',
|
|
324
|
-
'gateways', 'gatewayclasses', 'httproutes', 'grpcroutes', 'tcproutes', 'tlsroutes', 'sealedsecrets', 'workflowtemplates',
|
|
326
|
+
'gateways', 'gatewayclasses', 'httproutes', 'grpcroutes', 'tcproutes', 'tlsroutes', 'sealedsecrets', 'workflowtemplates', 'clusterworkflowtemplates',
|
|
325
327
|
'networkpolicies', 'networkpolicy',
|
|
326
328
|
'ciliumnetworkpolicies', 'ciliumnetworkpolicy', 'ciliumclusterwidenetworkpolicies', 'ciliumclusterwidenetworkpolicy',
|
|
327
329
|
'clusternetworkpolicies', 'clusternetworkpolicy',
|
|
@@ -406,6 +408,8 @@ interface ResourceRendererDispatchProps {
|
|
|
406
408
|
eventsHint?: React.ReactNode
|
|
407
409
|
/** When provided, sidebar sections (related resources, events, labels, annotations, metadata) are passed to this render prop instead of being rendered inline */
|
|
408
410
|
renderSidebar?: (sections: React.ReactNode) => React.ReactNode
|
|
411
|
+
/** Additional overview content that belongs in the main content column after the resource renderer sections. */
|
|
412
|
+
mainFooter?: React.ReactNode
|
|
409
413
|
/** K8s events for the focused resource — always shown (no toggle hides them)
|
|
410
414
|
* so resource history can't go missing. */
|
|
411
415
|
events?: TimelineEvent[]
|
|
@@ -440,6 +444,7 @@ export function ResourceRendererDispatch({
|
|
|
440
444
|
onOpenLogs,
|
|
441
445
|
eventsHint,
|
|
442
446
|
renderSidebar,
|
|
447
|
+
mainFooter,
|
|
443
448
|
events,
|
|
444
449
|
eventsLoading,
|
|
445
450
|
updates,
|
|
@@ -527,12 +532,13 @@ export function ResourceRendererDispatch({
|
|
|
527
532
|
{kind === 'secrets' && <SecretRenderer data={data} certificateInfo={certificateInfo} resourceData={data} onSaveSecretValue={onSaveSecretValue} isSaving={isSavingSecret} />}
|
|
528
533
|
{kind === 'jobs' && <JobRenderer data={data} />}
|
|
529
534
|
{kind === 'cronjobs' && <CronJobRenderer data={data} onNavigate={onNavigate} />}
|
|
535
|
+
{kind === 'cronworkflows' && <CronWorkflowRenderer data={data} onNavigate={onNavigate} />}
|
|
530
536
|
{(kind === 'hpas' || kind === 'horizontalpodautoscalers') && <HPAComp data={data} onNavigate={onNavigate} hpaDiagnosis={hpaDiagnosis} />}
|
|
531
537
|
{kind === 'nodes' && <NodeComp data={data} relationships={relationships} />}
|
|
532
538
|
{kind === 'persistentvolumeclaims' && <PVCComp data={data} onNavigate={onNavigate} />}
|
|
533
539
|
{kind === 'rollouts' && <RolloutRenderer data={data} />}
|
|
534
540
|
{kind === 'certificates' && !data?.apiVersion?.includes('networking.internal.knative.dev') && <CertificateRenderer data={data} />}
|
|
535
|
-
{kind === 'workflows' && <WorkflowRenderer data={data} />}
|
|
541
|
+
{kind === 'workflows' && <WorkflowRenderer data={data} onNavigate={onNavigate} />}
|
|
536
542
|
{kind === 'persistentvolumes' && <PersistentVolumeRenderer data={data} onNavigate={onNavigate} />}
|
|
537
543
|
{kind === 'storageclasses' && <StorageClassRenderer data={data} />}
|
|
538
544
|
{kind === 'certificaterequests' && <CertificateRequestRenderer data={data} />}
|
|
@@ -547,7 +553,7 @@ export function ResourceRendererDispatch({
|
|
|
547
553
|
{kind === 'tcproutes' && <SimpleRouteRenderer data={data} kind="TCPRoute" onNavigate={onNavigate} />}
|
|
548
554
|
{kind === 'tlsroutes' && <SimpleRouteRenderer data={data} kind="TLSRoute" onNavigate={onNavigate} />}
|
|
549
555
|
{kind === 'sealedsecrets' && <SealedSecretRenderer data={data} onNavigate={onNavigate} />}
|
|
550
|
-
{kind === 'workflowtemplates' && <WorkflowTemplateRenderer data={data} />}
|
|
556
|
+
{(kind === 'workflowtemplates' || kind === 'clusterworkflowtemplates') && <WorkflowTemplateRenderer data={data} />}
|
|
551
557
|
{(kind === 'networkpolicies' || kind === 'networkpolicy') && <NetworkPolicyRenderer data={data} />}
|
|
552
558
|
{(kind === 'ciliumnetworkpolicies' || kind === 'ciliumnetworkpolicy' || kind === 'ciliumclusterwidenetworkpolicies' || kind === 'ciliumclusterwidenetworkpolicy') && <CiliumNetworkPolicyRenderer data={data} />}
|
|
553
559
|
{(kind === 'clusternetworkpolicies' || kind === 'clusternetworkpolicy') && <ClusterNetworkPolicyRenderer data={data} />}
|
|
@@ -712,6 +718,7 @@ export function ResourceRendererDispatch({
|
|
|
712
718
|
{!renderSidebar && sidebarContent}
|
|
713
719
|
</>
|
|
714
720
|
)}
|
|
721
|
+
{mainFooter}
|
|
715
722
|
</div>
|
|
716
723
|
{renderSidebar && sidebarContent && renderSidebar(sidebarContent)}
|
|
717
724
|
</div>
|
|
@@ -722,6 +729,27 @@ export function ResourceRendererDispatch({
|
|
|
722
729
|
// RESOURCE STATUS HELPER
|
|
723
730
|
// ============================================================================
|
|
724
731
|
|
|
732
|
+
// Coarse health hint for the per-resource Diagnose entry point: should the action
|
|
733
|
+
// read as an urgent "Diagnose" (resource has a live problem) or a quiet "ask AI"?
|
|
734
|
+
// Derived from the same status the detail badge shows, so the button matches what
|
|
735
|
+
// the user sees. We key off the StatusBadge `level` (the workload/pod status fns
|
|
736
|
+
// return it) — NOT the color string, which varies by helper. 'unknown' for kinds
|
|
737
|
+
// without a level (we don't assert "Diagnose" when we can't tell → quiet variant).
|
|
738
|
+
export type DiagnoseHealthHint = 'problem' | 'healthy' | 'unknown'
|
|
739
|
+
export function diagnoseHealthHint(kind: string, data: any): DiagnoseHealthHint {
|
|
740
|
+
const st = getResourceStatus(kind, data) as { level?: string } | null
|
|
741
|
+
switch (st?.level) {
|
|
742
|
+
case 'unhealthy':
|
|
743
|
+
case 'degraded':
|
|
744
|
+
case 'alert':
|
|
745
|
+
return 'problem'
|
|
746
|
+
case 'healthy':
|
|
747
|
+
return 'healthy'
|
|
748
|
+
default:
|
|
749
|
+
return 'unknown'
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
725
753
|
export function getResourceStatus(kind: string, data: any): { text: string; color: string } | null {
|
|
726
754
|
if (!data) return null
|
|
727
755
|
const k = kind.toLowerCase()
|
|
@@ -752,6 +780,7 @@ export function getResourceStatus(kind: string, data: any): { text: string; colo
|
|
|
752
780
|
if (k === 'persistentvolumeclaims') return getPVCStatus(data)
|
|
753
781
|
if (k === 'rollouts') return getRolloutStatus(data)
|
|
754
782
|
if (k === 'workflows') return getWorkflowStatus(data)
|
|
783
|
+
if (k === 'cronworkflows') return getCronWorkflowStatus(data)
|
|
755
784
|
if (k === 'certificates') {
|
|
756
785
|
if (data.apiVersion?.includes('networking.internal.knative.dev')) {
|
|
757
786
|
const status = getKnativeConditionStatus(data)
|