@skyhook-io/radar-app 1.1.1 → 1.1.2
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 +1 -1
- package/src/App.tsx +81 -18
- package/src/api/client.ts +165 -11
- package/src/api/rbac.ts +57 -0
- package/src/components/compare/CompareViewRoute.tsx +116 -0
- package/src/components/compare/useCompareCandidates.ts +27 -0
- package/src/components/compare/useCompareLauncher.tsx +76 -0
- package/src/components/cost/CostView.tsx +1 -1
- package/src/components/gitops/GitOpsView.tsx +1 -1
- package/src/components/helm/InstallWizard.tsx +5 -5
- package/src/components/helm/ValuesViewer.tsx +3 -39
- package/src/components/home/HomeView.tsx +18 -2
- package/src/components/resource/HPACharts.tsx +232 -0
- package/src/components/resource/PVCUsageBar.tsx +59 -0
- package/src/components/resource/PrometheusCharts.tsx +151 -434
- package/src/components/resource/PrometheusChartsGrid.tsx +339 -0
- package/src/components/resource/RestartChart.tsx +124 -0
- package/src/components/resource/RightsizingStrip.tsx +167 -0
- package/src/components/resources/CompositeRenderer.tsx +101 -0
- package/src/components/resources/renderers/HPARenderer.tsx +17 -1
- package/src/components/resources/renderers/NamespaceRenderer.tsx +22 -0
- package/src/components/resources/renderers/PVCRenderer.tsx +19 -1
- package/src/components/resources/renderers/PodRenderer.tsx +13 -0
- package/src/components/resources/renderers/RoleBindingRenderer.tsx +43 -1
- package/src/components/resources/renderers/RoleRenderer.tsx +27 -1
- package/src/components/resources/renderers/ServiceAccountRenderer.tsx +28 -1
- package/src/components/resources/renderers/WorkloadRenderer.tsx +12 -0
- package/src/components/resources/renderers/index.ts +1 -0
- package/src/components/settings/MyPermissionsDialog.tsx +231 -0
- package/src/components/ui/DiagnosticsOverlay.tsx +1 -0
- package/src/components/workload/WorkloadView.tsx +107 -3
- package/src/context/NavCustomization.tsx +13 -0
|
@@ -23,6 +23,9 @@ import {
|
|
|
23
23
|
fetchJSON,
|
|
24
24
|
} from '../../api/client'
|
|
25
25
|
import { PrometheusCharts, isPrometheusSupported } from '../resource/PrometheusCharts'
|
|
26
|
+
import { PrometheusChartsGrid } from '../resource/PrometheusChartsGrid'
|
|
27
|
+
import { RestartEventLane } from '../resource/RestartChart'
|
|
28
|
+
import { RightsizingStrip } from '../resource/RightsizingStrip'
|
|
26
29
|
import { useResourceAudit, useResources } from '../../api/client'
|
|
27
30
|
import { AuditAlerts } from '@skyhook-io/k8s-ui'
|
|
28
31
|
import { WorkloadLogsViewer } from '../logs/WorkloadLogsViewer'
|
|
@@ -35,15 +38,30 @@ import { PodRenderer } from '../resources/renderers/PodRenderer'
|
|
|
35
38
|
import { NodeRenderer } from '../resources/renderers/NodeRenderer'
|
|
36
39
|
import { ServiceRenderer } from '../resources/renderers/ServiceRenderer'
|
|
37
40
|
import { WorkloadRenderer } from '../resources/renderers/WorkloadRenderer'
|
|
41
|
+
import { CompositeRenderer } from '../resources/CompositeRenderer'
|
|
42
|
+
import { ServiceAccountRenderer } from '../resources/renderers/ServiceAccountRenderer'
|
|
43
|
+
import { RoleRenderer } from '../resources/renderers/RoleRenderer'
|
|
44
|
+
import { RoleBindingRenderer } from '../resources/renderers/RoleBindingRenderer'
|
|
45
|
+
import { NamespaceRenderer } from '../resources/renderers/NamespaceRenderer'
|
|
46
|
+
import { HPARenderer } from '../resources/renderers/HPARenderer'
|
|
47
|
+
import { PVCRenderer } from '../resources/renderers/PVCRenderer'
|
|
38
48
|
import { CreateResourceDialog } from '../shared/CreateResourceDialog'
|
|
39
49
|
import { cleanYamlForDuplicate } from '../../utils/skeleton-yaml'
|
|
40
50
|
import { useDesktopDownload } from '../../hooks/useDesktopDownload'
|
|
51
|
+
import { useCompareLauncher } from '../compare/useCompareLauncher'
|
|
52
|
+
import { apiVersionToGroup } from '../../utils/navigation'
|
|
41
53
|
|
|
42
54
|
type TabType = 'overview' | 'timeline' | 'logs' | 'metrics' | 'yaml'
|
|
43
55
|
|
|
44
56
|
// Stable reference — web renderer wrappers inject platform hooks internally
|
|
45
57
|
const rendererOverrides: RendererOverrides = {
|
|
46
|
-
PodRenderer, NodeRenderer, ServiceRenderer, WorkloadRenderer,
|
|
58
|
+
PodRenderer, NodeRenderer, ServiceRenderer, WorkloadRenderer, CompositeRenderer,
|
|
59
|
+
ServiceAccountRenderer,
|
|
60
|
+
RoleRenderer,
|
|
61
|
+
RoleBindingRenderer,
|
|
62
|
+
NamespaceRenderer,
|
|
63
|
+
HPARenderer,
|
|
64
|
+
PVCRenderer,
|
|
47
65
|
}
|
|
48
66
|
|
|
49
67
|
// ============================================================================
|
|
@@ -323,9 +341,27 @@ export function WorkloadView({
|
|
|
323
341
|
// RBAC
|
|
324
342
|
const canUpdateSecrets = useCanUpdateSecrets()
|
|
325
343
|
const updateResource = useUpdateResource()
|
|
326
|
-
const
|
|
344
|
+
const baseActionsBarProps = useActionsBarProps(kindProp, namespace, name)
|
|
327
345
|
const desktopDownload = useDesktopDownload()
|
|
328
346
|
|
|
347
|
+
const resourceGroup = useMemo(
|
|
348
|
+
() => (resource?.apiVersion ? apiVersionToGroup(resource.apiVersion) : undefined),
|
|
349
|
+
[resource?.apiVersion],
|
|
350
|
+
)
|
|
351
|
+
const { onCompareTo, onCompareAcrossClusters, picker: comparePicker } = useCompareLauncher({
|
|
352
|
+
kind: kindProp,
|
|
353
|
+
namespace,
|
|
354
|
+
name,
|
|
355
|
+
// Prefer the URL-supplied group so Compare works even before the resource
|
|
356
|
+
// fetch completes; fall back to the derived group for callers that don't
|
|
357
|
+
// pass one.
|
|
358
|
+
group: rest.group || resourceGroup || undefined,
|
|
359
|
+
})
|
|
360
|
+
const actionsBarProps = useMemo(
|
|
361
|
+
() => ({ ...baseActionsBarProps, onCompareTo, onCompareAcrossClusters }),
|
|
362
|
+
[baseActionsBarProps, onCompareTo, onCompareAcrossClusters],
|
|
363
|
+
)
|
|
364
|
+
|
|
329
365
|
const handleUpdateResource = useCallback(async (params: { kind: string; namespace: string; name: string; yaml: string }) => {
|
|
330
366
|
await updateResource.mutateAsync(params)
|
|
331
367
|
}, [updateResource])
|
|
@@ -384,7 +420,7 @@ export function WorkloadView({
|
|
|
384
420
|
// Render props
|
|
385
421
|
renderLogsTab={(props) => <LogsTabContent {...props} />}
|
|
386
422
|
renderMetricsTab={({ kind, namespace: ns, name: n }) => (
|
|
387
|
-
<
|
|
423
|
+
<MetricsTabContent kind={kind} namespace={ns} name={n} resource={resource} expanded={expanded} />
|
|
388
424
|
)}
|
|
389
425
|
isMetricsAvailable={(kind, res) =>
|
|
390
426
|
isPrometheusSupported(kind) && !(kind === 'Pod' && res?.status?.phase === 'Pending')
|
|
@@ -412,6 +448,7 @@ export function WorkloadView({
|
|
|
412
448
|
rest.onNavigateToResource?.({ kind: kindToPlural(result.kind), namespace: result.namespace, name: result.name, group: '' })
|
|
413
449
|
}}
|
|
414
450
|
/>
|
|
451
|
+
{comparePicker}
|
|
415
452
|
</>
|
|
416
453
|
)
|
|
417
454
|
}
|
|
@@ -651,6 +688,73 @@ function FluxSourceConsumersInner({ sourceKind, namespace, name }: { sourceKind:
|
|
|
651
688
|
)
|
|
652
689
|
}
|
|
653
690
|
|
|
691
|
+
// Drawer mode: single chart + category tabs (compact for ~500px width).
|
|
692
|
+
// Full-screen mode: multi-chart grid so CPU + Memory + Network can be
|
|
693
|
+
// compared side-by-side without tab switching.
|
|
694
|
+
function MetricsTabContent({ kind, namespace, name, resource, expanded }: {
|
|
695
|
+
kind: string
|
|
696
|
+
namespace: string
|
|
697
|
+
name: string
|
|
698
|
+
resource: any
|
|
699
|
+
expanded: boolean
|
|
700
|
+
}) {
|
|
701
|
+
const showRightsizing = expanded && ['Deployment', 'StatefulSet', 'DaemonSet'].includes(kind)
|
|
702
|
+
|
|
703
|
+
if (expanded) {
|
|
704
|
+
return (
|
|
705
|
+
<div className="flex flex-col h-full">
|
|
706
|
+
{showRightsizing && (
|
|
707
|
+
<div className="px-4 pt-4">
|
|
708
|
+
<RightsizingStrip kind={kind} namespace={namespace} name={name} />
|
|
709
|
+
</div>
|
|
710
|
+
)}
|
|
711
|
+
<div className="flex-1 min-h-0">
|
|
712
|
+
<PrometheusChartsGrid
|
|
713
|
+
kind={kind}
|
|
714
|
+
namespace={namespace}
|
|
715
|
+
name={name}
|
|
716
|
+
resource={resource}
|
|
717
|
+
/>
|
|
718
|
+
</div>
|
|
719
|
+
</div>
|
|
720
|
+
)
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// Drawer fallback: single chart with tabs + restart lane below. The chart's
|
|
724
|
+
// time-range selector is mirrored to the restart lane so they stay aligned.
|
|
725
|
+
return (
|
|
726
|
+
<DrawerMetricsContent
|
|
727
|
+
kind={kind}
|
|
728
|
+
namespace={namespace}
|
|
729
|
+
name={name}
|
|
730
|
+
resource={resource}
|
|
731
|
+
/>
|
|
732
|
+
)
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function DrawerMetricsContent({ kind, namespace, name, resource }: {
|
|
736
|
+
kind: string
|
|
737
|
+
namespace: string
|
|
738
|
+
name: string
|
|
739
|
+
resource: any
|
|
740
|
+
}) {
|
|
741
|
+
const [chartRange, setChartRange] = useState<import('../../api/client').PrometheusTimeRange>('1h')
|
|
742
|
+
const showRestartLane = kind !== 'Node'
|
|
743
|
+
|
|
744
|
+
return (
|
|
745
|
+
<div className="flex flex-col h-full">
|
|
746
|
+
<div className="flex-1 min-h-0">
|
|
747
|
+
<PrometheusCharts kind={kind} namespace={namespace} name={name} showEmptyState resource={resource} onTimeRangeChange={setChartRange} />
|
|
748
|
+
</div>
|
|
749
|
+
{showRestartLane && (
|
|
750
|
+
<div className="px-4 pb-4">
|
|
751
|
+
<RestartEventLane kind={kind} namespace={namespace} name={name} range={chartRange} />
|
|
752
|
+
</div>
|
|
753
|
+
)}
|
|
754
|
+
</div>
|
|
755
|
+
)
|
|
756
|
+
}
|
|
757
|
+
|
|
654
758
|
// FLUX_SOURCE_KIND_BY_LOWER maps lowercase kind (what the inner WorkloadView
|
|
655
759
|
// produces via its plural-to-singular fallback) to the wire-correct
|
|
656
760
|
// PascalCase form that consumers carry in spec.sourceRef.kind. HelmChart is
|
|
@@ -18,6 +18,19 @@ interface NavCustomizationBase {
|
|
|
18
18
|
brandSlot?: ReactNode;
|
|
19
19
|
/** Replaces the ContextSwitcher (kubeconfig-context picker). */
|
|
20
20
|
contextSlot?: ReactNode;
|
|
21
|
+
/**
|
|
22
|
+
* When set, a "Compare across clusters" option is added to the Compare
|
|
23
|
+
* button in resource action bars. The host returns the URL that should
|
|
24
|
+
* be navigated to (via window.location.assign — typically a hub fleet
|
|
25
|
+
* route). Standalone Radar omits this and the compare action stays
|
|
26
|
+
* single-cluster.
|
|
27
|
+
*/
|
|
28
|
+
crossClusterCompareHref?: (ref: {
|
|
29
|
+
kind: string;
|
|
30
|
+
namespace: string;
|
|
31
|
+
name: string;
|
|
32
|
+
group?: string;
|
|
33
|
+
}) => string;
|
|
21
34
|
}
|
|
22
35
|
|
|
23
36
|
/**
|