@skyhook-io/k8s-ui 1.0.0
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 +63 -0
- package/src/components/dock/BottomDock.tsx +204 -0
- package/src/components/dock/DockContext.tsx +212 -0
- package/src/components/dock/TerminalTab.tsx +333 -0
- package/src/components/dock/index.ts +3 -0
- package/src/components/gitops/GitOpsActions.tsx +227 -0
- package/src/components/gitops/GitOpsStatusBadge.tsx +221 -0
- package/src/components/gitops/ManagedResourcesList.tsx +210 -0
- package/src/components/gitops/SyncCountdown.tsx +146 -0
- package/src/components/gitops/index.ts +4 -0
- package/src/components/logs/JsonLogLine.tsx +90 -0
- package/src/components/logs/LogCore.tsx +553 -0
- package/src/components/logs/LogToolbarSelects.tsx +74 -0
- package/src/components/logs/LogsViewer.tsx +146 -0
- package/src/components/logs/WorkloadLogsViewer.tsx +263 -0
- package/src/components/logs/index.ts +13 -0
- package/src/components/logs/useLogBuffer.ts +138 -0
- package/src/components/logs/useLogSearch.ts +151 -0
- package/src/components/logs/useLogStream.ts +61 -0
- package/src/components/resources/ResourcesView.tsx +5494 -0
- package/src/components/resources/index.ts +17 -0
- package/src/components/resources/renderers/AlertRenderer.tsx +166 -0
- package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +233 -0
- package/src/components/resources/renderers/CNPGBackupRenderer.tsx +89 -0
- package/src/components/resources/renderers/CNPGClusterRenderer.tsx +194 -0
- package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +79 -0
- package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +68 -0
- package/src/components/resources/renderers/CertificateRenderer.tsx +209 -0
- package/src/components/resources/renderers/CertificateRequestRenderer.tsx +123 -0
- package/src/components/resources/renderers/ChallengeRenderer.tsx +111 -0
- package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +164 -0
- package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +140 -0
- package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +153 -0
- package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +221 -0
- package/src/components/resources/renderers/ConfigMapRenderer.tsx +124 -0
- package/src/components/resources/renderers/CronJobRenderer.tsx +86 -0
- package/src/components/resources/renderers/EventRenderer.tsx +175 -0
- package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +119 -0
- package/src/components/resources/renderers/ExternalSecretRenderer.tsx +172 -0
- package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +242 -0
- package/src/components/resources/renderers/GRPCRouteRenderer.tsx +263 -0
- package/src/components/resources/renderers/GatewayClassRenderer.tsx +68 -0
- package/src/components/resources/renderers/GatewayRenderer.tsx +218 -0
- package/src/components/resources/renderers/GenericRenderer.tsx +476 -0
- package/src/components/resources/renderers/GitRepositoryRenderer.tsx +154 -0
- package/src/components/resources/renderers/HPARenderer.tsx +164 -0
- package/src/components/resources/renderers/HTTPRouteRenderer.tsx +303 -0
- package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +167 -0
- package/src/components/resources/renderers/IngressClassRenderer.tsx +49 -0
- package/src/components/resources/renderers/IngressRenderer.tsx +104 -0
- package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +175 -0
- package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +176 -0
- package/src/components/resources/renderers/IstioGatewayRenderer.tsx +125 -0
- package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +84 -0
- package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +112 -0
- package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +288 -0
- package/src/components/resources/renderers/JobRenderer.tsx +103 -0
- package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +157 -0
- package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +188 -0
- package/src/components/resources/renderers/KarpenterNodePoolRenderer.tsx +148 -0
- package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +90 -0
- package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +136 -0
- package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +151 -0
- package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +70 -0
- package/src/components/resources/renderers/KnativeEventingRenderer.tsx +309 -0
- package/src/components/resources/renderers/KnativeFlowRenderer.tsx +142 -0
- package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +232 -0
- package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +97 -0
- package/src/components/resources/renderers/KnativeRouteRenderer.tsx +74 -0
- package/src/components/resources/renderers/KnativeServiceRenderer.tsx +138 -0
- package/src/components/resources/renderers/KnativeSourceRenderer.tsx +220 -0
- package/src/components/resources/renderers/KustomizationRenderer.tsx +151 -0
- package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +352 -0
- package/src/components/resources/renderers/LeaseRenderer.tsx +93 -0
- package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +208 -0
- package/src/components/resources/renderers/NodeRenderer.tsx +282 -0
- package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +153 -0
- package/src/components/resources/renderers/OrderRenderer.tsx +108 -0
- package/src/components/resources/renderers/PVCRenderer.tsx +98 -0
- package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +138 -0
- package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +126 -0
- package/src/components/resources/renderers/PodMonitorRenderer.tsx +63 -0
- package/src/components/resources/renderers/PodRenderer.tsx +677 -0
- package/src/components/resources/renderers/PriorityClassRenderer.tsx +50 -0
- package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +62 -0
- package/src/components/resources/renderers/ReplicaSetRenderer.tsx +71 -0
- package/src/components/resources/renderers/RoleBindingRenderer.tsx +97 -0
- package/src/components/resources/renderers/RoleRenderer.tsx +192 -0
- package/src/components/resources/renderers/RolloutRenderer.tsx +227 -0
- package/src/components/resources/renderers/RuntimeClassRenderer.tsx +75 -0
- package/src/components/resources/renderers/SbomReportRenderer.tsx +98 -0
- package/src/components/resources/renderers/SealedSecretRenderer.tsx +121 -0
- package/src/components/resources/renderers/SecretRenderer.tsx +321 -0
- package/src/components/resources/renderers/SecretStoreRenderer.tsx +111 -0
- package/src/components/resources/renderers/ServiceAccountRenderer.tsx +81 -0
- package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +65 -0
- package/src/components/resources/renderers/ServiceRenderer.tsx +98 -0
- package/src/components/resources/renderers/SimpleRouteRenderer.tsx +195 -0
- package/src/components/resources/renderers/StorageClassRenderer.tsx +82 -0
- package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +257 -0
- package/src/components/resources/renderers/VPARenderer.tsx +193 -0
- package/src/components/resources/renderers/VeleroBSLRenderer.tsx +73 -0
- package/src/components/resources/renderers/VeleroBackupRenderer.tsx +193 -0
- package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +161 -0
- package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +134 -0
- package/src/components/resources/renderers/VeleroVSLRenderer.tsx +36 -0
- package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +347 -0
- package/src/components/resources/renderers/WebhookConfigRenderer.tsx +156 -0
- package/src/components/resources/renderers/WorkflowRenderer.tsx +202 -0
- package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +91 -0
- package/src/components/resources/renderers/WorkloadRenderer.tsx +311 -0
- package/src/components/resources/renderers/argo-cells.tsx +118 -0
- package/src/components/resources/renderers/certmanager-cells.tsx +164 -0
- package/src/components/resources/renderers/cnpg-cells.tsx +161 -0
- package/src/components/resources/renderers/eso-cells.tsx +115 -0
- package/src/components/resources/renderers/flux-cells.tsx +253 -0
- package/src/components/resources/renderers/index.ts +105 -0
- package/src/components/resources/renderers/istio-cells.tsx +210 -0
- package/src/components/resources/renderers/karpenter-cells.tsx +107 -0
- package/src/components/resources/renderers/keda-cells.tsx +94 -0
- package/src/components/resources/renderers/knative-cells.tsx +396 -0
- package/src/components/resources/renderers/kyverno-cells.tsx +85 -0
- package/src/components/resources/renderers/prometheus-cells.tsx +74 -0
- package/src/components/resources/renderers/traefik-cells.tsx +120 -0
- package/src/components/resources/renderers/trivy-cells.tsx +153 -0
- package/src/components/resources/renderers/trivy-shared.tsx +67 -0
- package/src/components/resources/renderers/velero-cells.tsx +164 -0
- package/src/components/resources/resource-utils-argo.ts +155 -0
- package/src/components/resources/resource-utils-certmanager.ts +198 -0
- package/src/components/resources/resource-utils-cnpg.ts +344 -0
- package/src/components/resources/resource-utils-eso.ts +385 -0
- package/src/components/resources/resource-utils-flux.ts +186 -0
- package/src/components/resources/resource-utils-istio.ts +372 -0
- package/src/components/resources/resource-utils-karpenter.ts +150 -0
- package/src/components/resources/resource-utils-keda.ts +203 -0
- package/src/components/resources/resource-utils-knative.ts +218 -0
- package/src/components/resources/resource-utils-kyverno.ts +224 -0
- package/src/components/resources/resource-utils-prometheus.ts +159 -0
- package/src/components/resources/resource-utils-traefik.ts +180 -0
- package/src/components/resources/resource-utils-trivy.ts +146 -0
- package/src/components/resources/resource-utils-velero.ts +293 -0
- package/src/components/resources/resource-utils.ts +1558 -0
- package/src/components/shared/EditableYamlView.tsx +302 -0
- package/src/components/shared/ResourceActionsBar.tsx +924 -0
- package/src/components/shared/ResourceRendererDispatch.tsx +557 -0
- package/src/components/shared/index.ts +3 -0
- package/src/components/timeline/index.ts +1 -0
- package/src/components/timeline/shared.tsx +614 -0
- package/src/components/topology/GroupNode.tsx +198 -0
- package/src/components/topology/K8sResourceNode.tsx +440 -0
- package/src/components/topology/TopologyControls.tsx +62 -0
- package/src/components/topology/TopologyFilterSidebar.tsx +377 -0
- package/src/components/topology/TopologyGraph.tsx +748 -0
- package/src/components/topology/TopologySearch.tsx +304 -0
- package/src/components/topology/index.ts +7 -0
- package/src/components/topology/layout.ts +734 -0
- package/src/components/topology/layout.worker.ts +272 -0
- package/src/components/topology/topology.css +129 -0
- package/src/components/ui/CodeViewer.tsx +451 -0
- package/src/components/ui/ConfirmDialog.tsx +188 -0
- package/src/components/ui/ForceDeleteConfirmDialog.tsx +57 -0
- package/src/components/ui/HealthRing.tsx +72 -0
- package/src/components/ui/MetricsChart.tsx +259 -0
- package/src/components/ui/ResourceBar.tsx +68 -0
- package/src/components/ui/Toast.tsx +225 -0
- package/src/components/ui/Tooltip.tsx +165 -0
- package/src/components/ui/YamlEditor.tsx +280 -0
- package/src/components/ui/drawer-components.tsx +792 -0
- package/src/components/ui/index.ts +10 -0
- package/src/components/workload/ResourceDetailDrawer.tsx +158 -0
- package/src/components/workload/WorkloadView.tsx +1109 -0
- package/src/components/workload/index.ts +2 -0
- package/src/hooks/index.ts +9 -0
- package/src/hooks/useAnimatedUnmount.ts +30 -0
- package/src/hooks/useKeyboardShortcuts.tsx +336 -0
- package/src/hooks/useRefreshAnimation.ts +58 -0
- package/src/index.ts +35 -0
- package/src/types/core.ts +894 -0
- package/src/types/gitops.ts +458 -0
- package/src/types/index.ts +2 -0
- package/src/utils/animation.ts +40 -0
- package/src/utils/api-resources.ts +185 -0
- package/src/utils/badge-colors.ts +399 -0
- package/src/utils/context-name.ts +72 -0
- package/src/utils/download.ts +19 -0
- package/src/utils/format.ts +233 -0
- package/src/utils/index.ts +10 -0
- package/src/utils/log-format.ts +300 -0
- package/src/utils/navigation.ts +119 -0
- package/src/utils/resource-hierarchy.ts +630 -0
- package/src/utils/resource-icons.ts +206 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { Globe, ArrowRight, Network } from 'lucide-react'
|
|
2
|
+
import { clsx } from 'clsx'
|
|
3
|
+
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceRefBadge } from '../../ui/drawer-components'
|
|
4
|
+
import type { ResourceRef } from '../../../types'
|
|
5
|
+
|
|
6
|
+
interface SimpleRouteRendererProps {
|
|
7
|
+
data: any
|
|
8
|
+
kind: 'TCPRoute' | 'TLSRoute'
|
|
9
|
+
onNavigate?: (ref: ResourceRef) => void
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function SimpleRouteRenderer({ data, kind, onNavigate }: SimpleRouteRendererProps) {
|
|
13
|
+
const spec = data.spec || {}
|
|
14
|
+
const status = data.status || {}
|
|
15
|
+
const parentRefs = spec.parentRefs || []
|
|
16
|
+
const hostnames = spec.hostnames || []
|
|
17
|
+
const rules = spec.rules || []
|
|
18
|
+
const parentStatuses = status.parents || []
|
|
19
|
+
const routeNs = data.metadata?.namespace || ''
|
|
20
|
+
|
|
21
|
+
const isTLS = kind === 'TLSRoute'
|
|
22
|
+
|
|
23
|
+
const notAcceptedParents = parentStatuses.filter((p: any) =>
|
|
24
|
+
(p.conditions || []).some((c: any) => c.type === 'Accepted' && c.status === 'False')
|
|
25
|
+
)
|
|
26
|
+
const unresolvedRefsParents = parentStatuses.filter((p: any) =>
|
|
27
|
+
(p.conditions || []).some((c: any) => c.type === 'ResolvedRefs' && c.status === 'False')
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
const firstParentConditions = parentStatuses.length > 0
|
|
31
|
+
? parentStatuses[0].conditions
|
|
32
|
+
: undefined
|
|
33
|
+
|
|
34
|
+
function toGatewayRef(ref: any): ResourceRef {
|
|
35
|
+
return {
|
|
36
|
+
kind: 'Gateway',
|
|
37
|
+
namespace: ref.namespace || routeNs,
|
|
38
|
+
name: ref.name,
|
|
39
|
+
group: 'gateway.networking.k8s.io',
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function toServiceRef(backend: any): ResourceRef {
|
|
44
|
+
return {
|
|
45
|
+
kind: 'Service',
|
|
46
|
+
namespace: backend.namespace || routeNs,
|
|
47
|
+
name: backend.name,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<>
|
|
53
|
+
{notAcceptedParents.length > 0 && (
|
|
54
|
+
<AlertBanner
|
|
55
|
+
variant="error"
|
|
56
|
+
title="Route Not Accepted"
|
|
57
|
+
message={notAcceptedParents.map((p: any) => {
|
|
58
|
+
const cond = (p.conditions || []).find((c: any) => c.type === 'Accepted' && c.status === 'False')
|
|
59
|
+
const gwName = p.parentRef?.name || 'unknown'
|
|
60
|
+
return cond?.reason
|
|
61
|
+
? `Gateway "${gwName}": ${cond.reason}${cond.message ? ' — ' + cond.message : ''}`
|
|
62
|
+
: `Gateway "${gwName}" has not accepted this route.`
|
|
63
|
+
}).join('; ')}
|
|
64
|
+
/>
|
|
65
|
+
)}
|
|
66
|
+
|
|
67
|
+
{unresolvedRefsParents.length > 0 && (
|
|
68
|
+
<AlertBanner
|
|
69
|
+
variant="warning"
|
|
70
|
+
title="Unresolved References"
|
|
71
|
+
message="Some backend references could not be resolved. Check that the target services exist and are accessible."
|
|
72
|
+
/>
|
|
73
|
+
)}
|
|
74
|
+
|
|
75
|
+
<Section title="Status" icon={Globe}>
|
|
76
|
+
<PropertyList>
|
|
77
|
+
{isTLS && (
|
|
78
|
+
<Property
|
|
79
|
+
label="SNI Hostnames"
|
|
80
|
+
value={
|
|
81
|
+
hostnames.length > 0 ? (
|
|
82
|
+
<div className="flex flex-wrap gap-1">
|
|
83
|
+
{hostnames.map((h: string) => (
|
|
84
|
+
<span key={h} className="px-2 py-0.5 bg-theme-elevated rounded text-xs text-theme-text-secondary">{h}</span>
|
|
85
|
+
))}
|
|
86
|
+
</div>
|
|
87
|
+
) : (
|
|
88
|
+
<span className="text-xs text-theme-text-tertiary">Any</span>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
/>
|
|
92
|
+
)}
|
|
93
|
+
<Property
|
|
94
|
+
label="Parent Gateways"
|
|
95
|
+
value={
|
|
96
|
+
parentRefs.length > 0 ? (
|
|
97
|
+
<div className="flex flex-wrap gap-1">
|
|
98
|
+
{parentRefs.map((ref: any, i: number) => (
|
|
99
|
+
<ResourceRefBadge key={`${ref.namespace || ''}-${ref.name}-${i}`} resourceRef={toGatewayRef(ref)} onClick={onNavigate} />
|
|
100
|
+
))}
|
|
101
|
+
</div>
|
|
102
|
+
) : 'None'
|
|
103
|
+
}
|
|
104
|
+
/>
|
|
105
|
+
</PropertyList>
|
|
106
|
+
</Section>
|
|
107
|
+
|
|
108
|
+
<Section title={`Rules (${rules.length})`} icon={Network} defaultExpanded>
|
|
109
|
+
<div className="space-y-1.5">
|
|
110
|
+
{rules.map((rule: any, ruleIdx: number) => {
|
|
111
|
+
const backendRefs = rule.backendRefs || []
|
|
112
|
+
const hasWeights = backendRefs.length > 1 && backendRefs.some((b: any) => b.weight !== undefined)
|
|
113
|
+
const totalWeight = hasWeights ? backendRefs.reduce((sum: number, b: any) => sum + (b.weight ?? 1), 0) : 0
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div key={ruleIdx} className="bg-theme-elevated/30 rounded p-2.5">
|
|
117
|
+
{rules.length > 1 && (
|
|
118
|
+
<div className="text-xs font-medium text-theme-text-tertiary mb-1.5">
|
|
119
|
+
Rule {ruleIdx + 1}
|
|
120
|
+
</div>
|
|
121
|
+
)}
|
|
122
|
+
|
|
123
|
+
{backendRefs.length > 0 ? (
|
|
124
|
+
<div className="text-xs text-theme-text-secondary flex flex-wrap items-center gap-1.5">
|
|
125
|
+
<ArrowRight className="w-3 h-3 text-theme-text-tertiary shrink-0" />
|
|
126
|
+
{backendRefs.map((b: any, bi: number) => {
|
|
127
|
+
const pct = hasWeights ? Math.round(((b.weight ?? 1) / totalWeight) * 100) : null
|
|
128
|
+
return (
|
|
129
|
+
<span key={bi} className="flex items-center gap-1">
|
|
130
|
+
<ResourceRefBadge resourceRef={toServiceRef(b)} onClick={onNavigate} />
|
|
131
|
+
{b.port && <span className="text-theme-text-tertiary">:{b.port}</span>}
|
|
132
|
+
{pct !== null && (
|
|
133
|
+
<span className="text-theme-text-tertiary text-[10px]">{pct}%</span>
|
|
134
|
+
)}
|
|
135
|
+
</span>
|
|
136
|
+
)
|
|
137
|
+
})}
|
|
138
|
+
</div>
|
|
139
|
+
) : (
|
|
140
|
+
<div className="text-xs text-theme-text-tertiary italic">No backends</div>
|
|
141
|
+
)}
|
|
142
|
+
</div>
|
|
143
|
+
)
|
|
144
|
+
})}
|
|
145
|
+
</div>
|
|
146
|
+
</Section>
|
|
147
|
+
|
|
148
|
+
{/* Parent Status */}
|
|
149
|
+
{parentStatuses.length > 0 && (
|
|
150
|
+
<Section title={`Parent Status (${parentStatuses.length})`} defaultExpanded>
|
|
151
|
+
<div className="space-y-2">
|
|
152
|
+
{parentStatuses.map((parent: any, idx: number) => {
|
|
153
|
+
const ref = parent.parentRef || {}
|
|
154
|
+
const conditions = parent.conditions || []
|
|
155
|
+
const accepted = conditions.find((c: any) => c.type === 'Accepted')
|
|
156
|
+
const resolved = conditions.find((c: any) => c.type === 'ResolvedRefs')
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<div key={`${ref.namespace || ''}-${ref.name}-${idx}`} className="bg-theme-elevated/30 rounded p-2.5">
|
|
160
|
+
<div className="text-sm font-medium text-theme-text-primary mb-1.5">
|
|
161
|
+
{ref.namespace ? `${ref.namespace}/` : ''}{ref.name || 'unknown'}
|
|
162
|
+
{ref.sectionName ? ` (${ref.sectionName})` : ''}
|
|
163
|
+
</div>
|
|
164
|
+
<div className="flex flex-wrap gap-2">
|
|
165
|
+
{accepted && (
|
|
166
|
+
<span className={clsx(
|
|
167
|
+
'px-2 py-0.5 rounded text-xs font-medium',
|
|
168
|
+
accepted.status === 'True' ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400'
|
|
169
|
+
)}>
|
|
170
|
+
{accepted.status === 'True' ? 'Accepted' : 'Not Accepted'}
|
|
171
|
+
</span>
|
|
172
|
+
)}
|
|
173
|
+
{resolved && (
|
|
174
|
+
<span className={clsx(
|
|
175
|
+
'px-2 py-0.5 rounded text-xs font-medium',
|
|
176
|
+
resolved.status === 'True' ? 'bg-green-500/20 text-green-400' : 'bg-yellow-500/20 text-yellow-400'
|
|
177
|
+
)}>
|
|
178
|
+
{resolved.status === 'True' ? 'Refs Resolved' : 'Unresolved Refs'}
|
|
179
|
+
</span>
|
|
180
|
+
)}
|
|
181
|
+
</div>
|
|
182
|
+
{accepted?.message && accepted.status === 'False' && (
|
|
183
|
+
<div className="text-xs text-theme-text-tertiary mt-1">{accepted.message}</div>
|
|
184
|
+
)}
|
|
185
|
+
</div>
|
|
186
|
+
)
|
|
187
|
+
})}
|
|
188
|
+
</div>
|
|
189
|
+
</Section>
|
|
190
|
+
)}
|
|
191
|
+
|
|
192
|
+
<ConditionsSection conditions={firstParentConditions} />
|
|
193
|
+
</>
|
|
194
|
+
)
|
|
195
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Database, Settings } from 'lucide-react'
|
|
2
|
+
import { Section, PropertyList, Property } from '../../ui/drawer-components'
|
|
3
|
+
|
|
4
|
+
interface StorageClassRendererProps {
|
|
5
|
+
data: any
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function StorageClassRenderer({ data }: StorageClassRendererProps) {
|
|
9
|
+
const parameters = data.parameters || {}
|
|
10
|
+
const mountOptions = data.mountOptions || []
|
|
11
|
+
const hasParameters = Object.keys(parameters).length > 0
|
|
12
|
+
const hasMountOptions = mountOptions.length > 0
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<>
|
|
16
|
+
<Section title="Configuration" icon={Database}>
|
|
17
|
+
<PropertyList>
|
|
18
|
+
<Property label="Provisioner" value={data.provisioner} />
|
|
19
|
+
<Property
|
|
20
|
+
label="Reclaim Policy"
|
|
21
|
+
value={
|
|
22
|
+
data.reclaimPolicy ? (
|
|
23
|
+
<span className={data.reclaimPolicy === 'Delete' ? 'text-red-400' : 'text-green-400'}>
|
|
24
|
+
{data.reclaimPolicy}
|
|
25
|
+
</span>
|
|
26
|
+
) : undefined
|
|
27
|
+
}
|
|
28
|
+
/>
|
|
29
|
+
<Property
|
|
30
|
+
label="Volume Binding Mode"
|
|
31
|
+
value={
|
|
32
|
+
data.volumeBindingMode ? (
|
|
33
|
+
<span title={
|
|
34
|
+
data.volumeBindingMode === 'WaitForFirstConsumer'
|
|
35
|
+
? 'Volume binding is delayed until a Pod using this PVC is created'
|
|
36
|
+
: undefined
|
|
37
|
+
}>
|
|
38
|
+
{data.volumeBindingMode}
|
|
39
|
+
</span>
|
|
40
|
+
) : undefined
|
|
41
|
+
}
|
|
42
|
+
/>
|
|
43
|
+
<Property
|
|
44
|
+
label="Allow Volume Expansion"
|
|
45
|
+
value={
|
|
46
|
+
data.allowVolumeExpansion != null ? (
|
|
47
|
+
<span className={data.allowVolumeExpansion ? 'text-green-400' : undefined}>
|
|
48
|
+
{data.allowVolumeExpansion ? 'Yes' : 'No'}
|
|
49
|
+
</span>
|
|
50
|
+
) : undefined
|
|
51
|
+
}
|
|
52
|
+
/>
|
|
53
|
+
</PropertyList>
|
|
54
|
+
</Section>
|
|
55
|
+
|
|
56
|
+
{hasParameters && (
|
|
57
|
+
<Section title="Parameters" icon={Settings}>
|
|
58
|
+
<PropertyList>
|
|
59
|
+
{Object.entries(parameters).map(([key, value]) => (
|
|
60
|
+
<Property key={key} label={key} value={value as string} />
|
|
61
|
+
))}
|
|
62
|
+
</PropertyList>
|
|
63
|
+
</Section>
|
|
64
|
+
)}
|
|
65
|
+
|
|
66
|
+
{hasMountOptions && (
|
|
67
|
+
<Section title="Mount Options">
|
|
68
|
+
<div className="flex flex-wrap gap-1.5">
|
|
69
|
+
{mountOptions.map((option: string) => (
|
|
70
|
+
<span
|
|
71
|
+
key={option}
|
|
72
|
+
className="px-2 py-0.5 rounded text-xs bg-blue-500/20 text-blue-400"
|
|
73
|
+
>
|
|
74
|
+
{option}
|
|
75
|
+
</span>
|
|
76
|
+
))}
|
|
77
|
+
</div>
|
|
78
|
+
</Section>
|
|
79
|
+
)}
|
|
80
|
+
</>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { Globe, Route, Layers, Lock } from 'lucide-react'
|
|
2
|
+
import { Section, PropertyList, Property, AlertBanner, ResourceLink } from '../../ui/drawer-components'
|
|
3
|
+
|
|
4
|
+
interface TraefikIngressRouteRendererProps {
|
|
5
|
+
data: any
|
|
6
|
+
onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function TraefikIngressRouteRenderer({ data, onNavigate }: TraefikIngressRouteRendererProps) {
|
|
10
|
+
const spec = data.spec || {}
|
|
11
|
+
const routes = spec.routes || []
|
|
12
|
+
const entryPoints = spec.entryPoints || []
|
|
13
|
+
const tls = spec.tls
|
|
14
|
+
const ns = data.metadata?.namespace || ''
|
|
15
|
+
|
|
16
|
+
// Collect unique services and middleware across all routes
|
|
17
|
+
const allServices: Array<{ name: string; kind: string; port?: number; namespace?: string; serversTransport?: string }> = []
|
|
18
|
+
const allMiddlewares: Array<{ name: string; namespace?: string }> = []
|
|
19
|
+
const seenSvc = new Set<string>()
|
|
20
|
+
const seenMw = new Set<string>()
|
|
21
|
+
|
|
22
|
+
for (const route of routes) {
|
|
23
|
+
for (const svc of route.services || []) {
|
|
24
|
+
const key = `${svc.kind || 'Service'}/${svc.namespace || ''}/${svc.name}`
|
|
25
|
+
if (!seenSvc.has(key)) {
|
|
26
|
+
seenSvc.add(key)
|
|
27
|
+
allServices.push(svc)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
for (const mw of route.middlewares || []) {
|
|
31
|
+
const key = `${mw.namespace || ''}/${mw.name}`
|
|
32
|
+
if (!seenMw.has(key)) {
|
|
33
|
+
seenMw.add(key)
|
|
34
|
+
allMiddlewares.push(mw)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const hasNoRoutes = routes.length === 0
|
|
40
|
+
const hasNoServices = allServices.length === 0 && routes.length > 0
|
|
41
|
+
|
|
42
|
+
// Determine kind label from apiVersion/kind
|
|
43
|
+
const kindLabel = data.kind || 'IngressRoute'
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<>
|
|
47
|
+
{hasNoRoutes && (
|
|
48
|
+
<AlertBanner
|
|
49
|
+
variant="warning"
|
|
50
|
+
title="No Routes Defined"
|
|
51
|
+
message={`This ${kindLabel} has no routes configured. No traffic will be routed.`}
|
|
52
|
+
/>
|
|
53
|
+
)}
|
|
54
|
+
|
|
55
|
+
{hasNoServices && (
|
|
56
|
+
<AlertBanner
|
|
57
|
+
variant="info"
|
|
58
|
+
title="No Backend Services"
|
|
59
|
+
message="Routes are defined but none reference backend services."
|
|
60
|
+
/>
|
|
61
|
+
)}
|
|
62
|
+
|
|
63
|
+
<Section title={kindLabel} icon={Globe} defaultExpanded>
|
|
64
|
+
<PropertyList>
|
|
65
|
+
<Property label="Entry Points" value={entryPoints.length > 0 ? entryPoints.join(', ') : '-'} />
|
|
66
|
+
<Property label="Routes" value={`${routes.length}`} />
|
|
67
|
+
<Property label="Services" value={`${allServices.length}`} />
|
|
68
|
+
<Property label="Middlewares" value={allMiddlewares.length > 0 ? `${allMiddlewares.length}` : 'None'} />
|
|
69
|
+
<Property label="TLS" value={tls ? 'Enabled' : 'None'} />
|
|
70
|
+
</PropertyList>
|
|
71
|
+
</Section>
|
|
72
|
+
|
|
73
|
+
<Section title={`Routes (${routes.length})`} icon={Route} defaultExpanded>
|
|
74
|
+
<div className="space-y-3">
|
|
75
|
+
{routes.map((route: any, i: number) => {
|
|
76
|
+
const services = route.services || []
|
|
77
|
+
const middlewares = route.middlewares || []
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<div key={i} className="bg-theme-elevated/30 rounded p-3">
|
|
81
|
+
{/* Match expression */}
|
|
82
|
+
<div className="flex items-start gap-2 mb-2">
|
|
83
|
+
<span className="text-sm font-medium text-theme-text-primary break-all">
|
|
84
|
+
{route.match || 'No match rule'}
|
|
85
|
+
</span>
|
|
86
|
+
{route.priority && (
|
|
87
|
+
<span className="px-1.5 py-0.5 bg-theme-hover rounded text-[10px] text-theme-text-secondary shrink-0">
|
|
88
|
+
priority: {route.priority}
|
|
89
|
+
</span>
|
|
90
|
+
)}
|
|
91
|
+
{route.kind && route.kind !== 'Rule' && (
|
|
92
|
+
<span className="px-1.5 py-0.5 bg-theme-hover rounded text-[10px] text-theme-text-secondary shrink-0">
|
|
93
|
+
{route.kind}
|
|
94
|
+
</span>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
{/* Services */}
|
|
99
|
+
{services.length > 0 && (
|
|
100
|
+
<div className="mb-2">
|
|
101
|
+
<div className="text-[10px] font-medium text-theme-text-tertiary uppercase tracking-wider mb-1">Services</div>
|
|
102
|
+
<div className="space-y-1">
|
|
103
|
+
{services.map((svc: any, si: number) => {
|
|
104
|
+
const svcKind = svc.kind || 'Service'
|
|
105
|
+
const svcNs = svc.namespace || ns
|
|
106
|
+
const isTraefikSvc = svcKind === 'TraefikService'
|
|
107
|
+
const port = svc.port ? `:${svc.port}` : ''
|
|
108
|
+
const weight = svc.weight !== undefined ? ` (${svc.weight}%)` : ''
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<div key={si} className="flex items-center gap-2 text-xs">
|
|
112
|
+
{isTraefikSvc && (
|
|
113
|
+
<span className="px-1.5 py-0.5 bg-cyan-500/10 text-cyan-400 rounded text-[10px]">
|
|
114
|
+
TraefikService
|
|
115
|
+
</span>
|
|
116
|
+
)}
|
|
117
|
+
<ResourceLink
|
|
118
|
+
name={svc.name}
|
|
119
|
+
kind={isTraefikSvc ? 'traefikservices' : 'services'}
|
|
120
|
+
namespace={svcNs}
|
|
121
|
+
label={<span className="text-blue-400">{svc.name}{port}{weight}</span>}
|
|
122
|
+
onNavigate={onNavigate}
|
|
123
|
+
/>
|
|
124
|
+
{svc.serversTransport && (
|
|
125
|
+
<span className="text-theme-text-tertiary">
|
|
126
|
+
transport: <ResourceLink
|
|
127
|
+
name={svc.serversTransport}
|
|
128
|
+
kind="serverstransports"
|
|
129
|
+
namespace={ns}
|
|
130
|
+
onNavigate={onNavigate}
|
|
131
|
+
/>
|
|
132
|
+
</span>
|
|
133
|
+
)}
|
|
134
|
+
</div>
|
|
135
|
+
)
|
|
136
|
+
})}
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
)}
|
|
140
|
+
|
|
141
|
+
{/* Middlewares */}
|
|
142
|
+
{middlewares.length > 0 && (
|
|
143
|
+
<div>
|
|
144
|
+
<div className="text-[10px] font-medium text-theme-text-tertiary uppercase tracking-wider mb-1">Middlewares</div>
|
|
145
|
+
<div className="flex flex-wrap gap-1">
|
|
146
|
+
{middlewares.map((mw: any, mi: number) => {
|
|
147
|
+
const mwNs = mw.namespace || ns
|
|
148
|
+
return (
|
|
149
|
+
<ResourceLink
|
|
150
|
+
key={mi}
|
|
151
|
+
name={mw.name}
|
|
152
|
+
kind="middlewares"
|
|
153
|
+
namespace={mwNs}
|
|
154
|
+
label={
|
|
155
|
+
<span className="px-1.5 py-0.5 bg-purple-500/10 text-purple-400 rounded text-[10px] inline-block">
|
|
156
|
+
{mw.namespace && mw.namespace !== ns ? `${mw.namespace}/` : ''}{mw.name}
|
|
157
|
+
</span>
|
|
158
|
+
}
|
|
159
|
+
onNavigate={onNavigate}
|
|
160
|
+
/>
|
|
161
|
+
)
|
|
162
|
+
})}
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
)}
|
|
166
|
+
</div>
|
|
167
|
+
)
|
|
168
|
+
})}
|
|
169
|
+
</div>
|
|
170
|
+
</Section>
|
|
171
|
+
|
|
172
|
+
{tls && (
|
|
173
|
+
<Section title="TLS" icon={Lock} defaultExpanded>
|
|
174
|
+
<PropertyList>
|
|
175
|
+
{tls.secretName && (
|
|
176
|
+
<Property label="Secret" value={
|
|
177
|
+
<ResourceLink
|
|
178
|
+
name={tls.secretName}
|
|
179
|
+
kind="secrets"
|
|
180
|
+
namespace={ns}
|
|
181
|
+
onNavigate={onNavigate}
|
|
182
|
+
/>
|
|
183
|
+
} />
|
|
184
|
+
)}
|
|
185
|
+
{tls.options?.name && (
|
|
186
|
+
<Property label="TLS Option" value={
|
|
187
|
+
<ResourceLink
|
|
188
|
+
name={tls.options.name}
|
|
189
|
+
kind="tlsoptions"
|
|
190
|
+
namespace={tls.options.namespace || ns}
|
|
191
|
+
onNavigate={onNavigate}
|
|
192
|
+
/>
|
|
193
|
+
} />
|
|
194
|
+
)}
|
|
195
|
+
{tls.store?.name && (
|
|
196
|
+
<Property label="TLS Store" value={
|
|
197
|
+
<ResourceLink
|
|
198
|
+
name={tls.store.name}
|
|
199
|
+
kind="tlsstores"
|
|
200
|
+
namespace={tls.store.namespace || ns}
|
|
201
|
+
onNavigate={onNavigate}
|
|
202
|
+
/>
|
|
203
|
+
} />
|
|
204
|
+
)}
|
|
205
|
+
{tls.certResolver && (
|
|
206
|
+
<Property label="Cert Resolver" value={tls.certResolver} />
|
|
207
|
+
)}
|
|
208
|
+
{tls.domains && tls.domains.length > 0 && (
|
|
209
|
+
<Property label="Domains" value={
|
|
210
|
+
<div className="space-y-1">
|
|
211
|
+
{tls.domains.map((d: any, i: number) => (
|
|
212
|
+
<div key={i} className="text-xs">
|
|
213
|
+
{d.main && <span className="text-theme-text-secondary">{d.main}</span>}
|
|
214
|
+
{d.sans && d.sans.length > 0 && (
|
|
215
|
+
<span className="text-theme-text-tertiary"> + {d.sans.length} SAN(s)</span>
|
|
216
|
+
)}
|
|
217
|
+
</div>
|
|
218
|
+
))}
|
|
219
|
+
</div>
|
|
220
|
+
} />
|
|
221
|
+
)}
|
|
222
|
+
{!tls.secretName && !tls.certResolver && (
|
|
223
|
+
<Property label="Mode" value="TLS termination (no explicit secret or resolver)" />
|
|
224
|
+
)}
|
|
225
|
+
</PropertyList>
|
|
226
|
+
</Section>
|
|
227
|
+
)}
|
|
228
|
+
|
|
229
|
+
{allMiddlewares.length > 0 && (
|
|
230
|
+
<Section title={`Middleware Chain (${allMiddlewares.length})`} icon={Layers}>
|
|
231
|
+
<div className="space-y-1">
|
|
232
|
+
{allMiddlewares.map((mw, i) => {
|
|
233
|
+
const mwNs = mw.namespace || ns
|
|
234
|
+
const isCrossNs = mw.namespace && mw.namespace !== ns
|
|
235
|
+
return (
|
|
236
|
+
<div key={i} className="flex items-center gap-2 text-xs">
|
|
237
|
+
<span className="text-theme-text-tertiary w-4 text-right">{i + 1}.</span>
|
|
238
|
+
<ResourceLink
|
|
239
|
+
name={mw.name}
|
|
240
|
+
kind="middlewares"
|
|
241
|
+
namespace={mwNs}
|
|
242
|
+
onNavigate={onNavigate}
|
|
243
|
+
/>
|
|
244
|
+
{isCrossNs && (
|
|
245
|
+
<span className="px-1.5 py-0.5 bg-yellow-500/10 text-yellow-400 rounded text-[10px]">
|
|
246
|
+
{mw.namespace}
|
|
247
|
+
</span>
|
|
248
|
+
)}
|
|
249
|
+
</div>
|
|
250
|
+
)
|
|
251
|
+
})}
|
|
252
|
+
</div>
|
|
253
|
+
</Section>
|
|
254
|
+
)}
|
|
255
|
+
</>
|
|
256
|
+
)
|
|
257
|
+
}
|