@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.
Files changed (193) hide show
  1. package/package.json +63 -0
  2. package/src/components/dock/BottomDock.tsx +204 -0
  3. package/src/components/dock/DockContext.tsx +212 -0
  4. package/src/components/dock/TerminalTab.tsx +333 -0
  5. package/src/components/dock/index.ts +3 -0
  6. package/src/components/gitops/GitOpsActions.tsx +227 -0
  7. package/src/components/gitops/GitOpsStatusBadge.tsx +221 -0
  8. package/src/components/gitops/ManagedResourcesList.tsx +210 -0
  9. package/src/components/gitops/SyncCountdown.tsx +146 -0
  10. package/src/components/gitops/index.ts +4 -0
  11. package/src/components/logs/JsonLogLine.tsx +90 -0
  12. package/src/components/logs/LogCore.tsx +553 -0
  13. package/src/components/logs/LogToolbarSelects.tsx +74 -0
  14. package/src/components/logs/LogsViewer.tsx +146 -0
  15. package/src/components/logs/WorkloadLogsViewer.tsx +263 -0
  16. package/src/components/logs/index.ts +13 -0
  17. package/src/components/logs/useLogBuffer.ts +138 -0
  18. package/src/components/logs/useLogSearch.ts +151 -0
  19. package/src/components/logs/useLogStream.ts +61 -0
  20. package/src/components/resources/ResourcesView.tsx +5494 -0
  21. package/src/components/resources/index.ts +17 -0
  22. package/src/components/resources/renderers/AlertRenderer.tsx +166 -0
  23. package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +233 -0
  24. package/src/components/resources/renderers/CNPGBackupRenderer.tsx +89 -0
  25. package/src/components/resources/renderers/CNPGClusterRenderer.tsx +194 -0
  26. package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +79 -0
  27. package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +68 -0
  28. package/src/components/resources/renderers/CertificateRenderer.tsx +209 -0
  29. package/src/components/resources/renderers/CertificateRequestRenderer.tsx +123 -0
  30. package/src/components/resources/renderers/ChallengeRenderer.tsx +111 -0
  31. package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +164 -0
  32. package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +140 -0
  33. package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +153 -0
  34. package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +221 -0
  35. package/src/components/resources/renderers/ConfigMapRenderer.tsx +124 -0
  36. package/src/components/resources/renderers/CronJobRenderer.tsx +86 -0
  37. package/src/components/resources/renderers/EventRenderer.tsx +175 -0
  38. package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +119 -0
  39. package/src/components/resources/renderers/ExternalSecretRenderer.tsx +172 -0
  40. package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +242 -0
  41. package/src/components/resources/renderers/GRPCRouteRenderer.tsx +263 -0
  42. package/src/components/resources/renderers/GatewayClassRenderer.tsx +68 -0
  43. package/src/components/resources/renderers/GatewayRenderer.tsx +218 -0
  44. package/src/components/resources/renderers/GenericRenderer.tsx +476 -0
  45. package/src/components/resources/renderers/GitRepositoryRenderer.tsx +154 -0
  46. package/src/components/resources/renderers/HPARenderer.tsx +164 -0
  47. package/src/components/resources/renderers/HTTPRouteRenderer.tsx +303 -0
  48. package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +167 -0
  49. package/src/components/resources/renderers/IngressClassRenderer.tsx +49 -0
  50. package/src/components/resources/renderers/IngressRenderer.tsx +104 -0
  51. package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +175 -0
  52. package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +176 -0
  53. package/src/components/resources/renderers/IstioGatewayRenderer.tsx +125 -0
  54. package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +84 -0
  55. package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +112 -0
  56. package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +288 -0
  57. package/src/components/resources/renderers/JobRenderer.tsx +103 -0
  58. package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +157 -0
  59. package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +188 -0
  60. package/src/components/resources/renderers/KarpenterNodePoolRenderer.tsx +148 -0
  61. package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +90 -0
  62. package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +136 -0
  63. package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +151 -0
  64. package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +70 -0
  65. package/src/components/resources/renderers/KnativeEventingRenderer.tsx +309 -0
  66. package/src/components/resources/renderers/KnativeFlowRenderer.tsx +142 -0
  67. package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +232 -0
  68. package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +97 -0
  69. package/src/components/resources/renderers/KnativeRouteRenderer.tsx +74 -0
  70. package/src/components/resources/renderers/KnativeServiceRenderer.tsx +138 -0
  71. package/src/components/resources/renderers/KnativeSourceRenderer.tsx +220 -0
  72. package/src/components/resources/renderers/KustomizationRenderer.tsx +151 -0
  73. package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +352 -0
  74. package/src/components/resources/renderers/LeaseRenderer.tsx +93 -0
  75. package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +208 -0
  76. package/src/components/resources/renderers/NodeRenderer.tsx +282 -0
  77. package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +153 -0
  78. package/src/components/resources/renderers/OrderRenderer.tsx +108 -0
  79. package/src/components/resources/renderers/PVCRenderer.tsx +98 -0
  80. package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +138 -0
  81. package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +126 -0
  82. package/src/components/resources/renderers/PodMonitorRenderer.tsx +63 -0
  83. package/src/components/resources/renderers/PodRenderer.tsx +677 -0
  84. package/src/components/resources/renderers/PriorityClassRenderer.tsx +50 -0
  85. package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +62 -0
  86. package/src/components/resources/renderers/ReplicaSetRenderer.tsx +71 -0
  87. package/src/components/resources/renderers/RoleBindingRenderer.tsx +97 -0
  88. package/src/components/resources/renderers/RoleRenderer.tsx +192 -0
  89. package/src/components/resources/renderers/RolloutRenderer.tsx +227 -0
  90. package/src/components/resources/renderers/RuntimeClassRenderer.tsx +75 -0
  91. package/src/components/resources/renderers/SbomReportRenderer.tsx +98 -0
  92. package/src/components/resources/renderers/SealedSecretRenderer.tsx +121 -0
  93. package/src/components/resources/renderers/SecretRenderer.tsx +321 -0
  94. package/src/components/resources/renderers/SecretStoreRenderer.tsx +111 -0
  95. package/src/components/resources/renderers/ServiceAccountRenderer.tsx +81 -0
  96. package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +65 -0
  97. package/src/components/resources/renderers/ServiceRenderer.tsx +98 -0
  98. package/src/components/resources/renderers/SimpleRouteRenderer.tsx +195 -0
  99. package/src/components/resources/renderers/StorageClassRenderer.tsx +82 -0
  100. package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +257 -0
  101. package/src/components/resources/renderers/VPARenderer.tsx +193 -0
  102. package/src/components/resources/renderers/VeleroBSLRenderer.tsx +73 -0
  103. package/src/components/resources/renderers/VeleroBackupRenderer.tsx +193 -0
  104. package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +161 -0
  105. package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +134 -0
  106. package/src/components/resources/renderers/VeleroVSLRenderer.tsx +36 -0
  107. package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +347 -0
  108. package/src/components/resources/renderers/WebhookConfigRenderer.tsx +156 -0
  109. package/src/components/resources/renderers/WorkflowRenderer.tsx +202 -0
  110. package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +91 -0
  111. package/src/components/resources/renderers/WorkloadRenderer.tsx +311 -0
  112. package/src/components/resources/renderers/argo-cells.tsx +118 -0
  113. package/src/components/resources/renderers/certmanager-cells.tsx +164 -0
  114. package/src/components/resources/renderers/cnpg-cells.tsx +161 -0
  115. package/src/components/resources/renderers/eso-cells.tsx +115 -0
  116. package/src/components/resources/renderers/flux-cells.tsx +253 -0
  117. package/src/components/resources/renderers/index.ts +105 -0
  118. package/src/components/resources/renderers/istio-cells.tsx +210 -0
  119. package/src/components/resources/renderers/karpenter-cells.tsx +107 -0
  120. package/src/components/resources/renderers/keda-cells.tsx +94 -0
  121. package/src/components/resources/renderers/knative-cells.tsx +396 -0
  122. package/src/components/resources/renderers/kyverno-cells.tsx +85 -0
  123. package/src/components/resources/renderers/prometheus-cells.tsx +74 -0
  124. package/src/components/resources/renderers/traefik-cells.tsx +120 -0
  125. package/src/components/resources/renderers/trivy-cells.tsx +153 -0
  126. package/src/components/resources/renderers/trivy-shared.tsx +67 -0
  127. package/src/components/resources/renderers/velero-cells.tsx +164 -0
  128. package/src/components/resources/resource-utils-argo.ts +155 -0
  129. package/src/components/resources/resource-utils-certmanager.ts +198 -0
  130. package/src/components/resources/resource-utils-cnpg.ts +344 -0
  131. package/src/components/resources/resource-utils-eso.ts +385 -0
  132. package/src/components/resources/resource-utils-flux.ts +186 -0
  133. package/src/components/resources/resource-utils-istio.ts +372 -0
  134. package/src/components/resources/resource-utils-karpenter.ts +150 -0
  135. package/src/components/resources/resource-utils-keda.ts +203 -0
  136. package/src/components/resources/resource-utils-knative.ts +218 -0
  137. package/src/components/resources/resource-utils-kyverno.ts +224 -0
  138. package/src/components/resources/resource-utils-prometheus.ts +159 -0
  139. package/src/components/resources/resource-utils-traefik.ts +180 -0
  140. package/src/components/resources/resource-utils-trivy.ts +146 -0
  141. package/src/components/resources/resource-utils-velero.ts +293 -0
  142. package/src/components/resources/resource-utils.ts +1558 -0
  143. package/src/components/shared/EditableYamlView.tsx +302 -0
  144. package/src/components/shared/ResourceActionsBar.tsx +924 -0
  145. package/src/components/shared/ResourceRendererDispatch.tsx +557 -0
  146. package/src/components/shared/index.ts +3 -0
  147. package/src/components/timeline/index.ts +1 -0
  148. package/src/components/timeline/shared.tsx +614 -0
  149. package/src/components/topology/GroupNode.tsx +198 -0
  150. package/src/components/topology/K8sResourceNode.tsx +440 -0
  151. package/src/components/topology/TopologyControls.tsx +62 -0
  152. package/src/components/topology/TopologyFilterSidebar.tsx +377 -0
  153. package/src/components/topology/TopologyGraph.tsx +748 -0
  154. package/src/components/topology/TopologySearch.tsx +304 -0
  155. package/src/components/topology/index.ts +7 -0
  156. package/src/components/topology/layout.ts +734 -0
  157. package/src/components/topology/layout.worker.ts +272 -0
  158. package/src/components/topology/topology.css +129 -0
  159. package/src/components/ui/CodeViewer.tsx +451 -0
  160. package/src/components/ui/ConfirmDialog.tsx +188 -0
  161. package/src/components/ui/ForceDeleteConfirmDialog.tsx +57 -0
  162. package/src/components/ui/HealthRing.tsx +72 -0
  163. package/src/components/ui/MetricsChart.tsx +259 -0
  164. package/src/components/ui/ResourceBar.tsx +68 -0
  165. package/src/components/ui/Toast.tsx +225 -0
  166. package/src/components/ui/Tooltip.tsx +165 -0
  167. package/src/components/ui/YamlEditor.tsx +280 -0
  168. package/src/components/ui/drawer-components.tsx +792 -0
  169. package/src/components/ui/index.ts +10 -0
  170. package/src/components/workload/ResourceDetailDrawer.tsx +158 -0
  171. package/src/components/workload/WorkloadView.tsx +1109 -0
  172. package/src/components/workload/index.ts +2 -0
  173. package/src/hooks/index.ts +9 -0
  174. package/src/hooks/useAnimatedUnmount.ts +30 -0
  175. package/src/hooks/useKeyboardShortcuts.tsx +336 -0
  176. package/src/hooks/useRefreshAnimation.ts +58 -0
  177. package/src/index.ts +35 -0
  178. package/src/types/core.ts +894 -0
  179. package/src/types/gitops.ts +458 -0
  180. package/src/types/index.ts +2 -0
  181. package/src/utils/animation.ts +40 -0
  182. package/src/utils/api-resources.ts +185 -0
  183. package/src/utils/badge-colors.ts +399 -0
  184. package/src/utils/context-name.ts +72 -0
  185. package/src/utils/download.ts +19 -0
  186. package/src/utils/format.ts +233 -0
  187. package/src/utils/index.ts +10 -0
  188. package/src/utils/log-format.ts +300 -0
  189. package/src/utils/navigation.ts +119 -0
  190. package/src/utils/resource-hierarchy.ts +630 -0
  191. package/src/utils/resource-icons.ts +206 -0
  192. package/src/vite-env.d.ts +1 -0
  193. package/tsconfig.json +24 -0
@@ -0,0 +1,49 @@
1
+ import { Globe } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property } from '../../ui/drawer-components'
4
+
5
+ interface IngressClassRendererProps {
6
+ data: any
7
+ }
8
+
9
+ export function IngressClassRenderer({ data }: IngressClassRendererProps) {
10
+ const spec = data.spec || {}
11
+ const annotations = data.metadata?.annotations || {}
12
+
13
+ const isDefault = annotations['ingressclass.kubernetes.io/is-default-class'] === 'true'
14
+
15
+ return (
16
+ <>
17
+ <Section title="Ingress Class" icon={Globe}>
18
+ <PropertyList>
19
+ <Property label="Controller" value={spec.controller} />
20
+ <Property
21
+ label="Default"
22
+ value={
23
+ <span className={clsx(
24
+ 'px-2 py-0.5 rounded text-xs font-medium',
25
+ isDefault
26
+ ? 'bg-green-500/20 text-green-400'
27
+ : 'bg-gray-500/20 text-gray-400'
28
+ )}>
29
+ {isDefault ? 'Yes' : 'No'}
30
+ </span>
31
+ }
32
+ />
33
+ </PropertyList>
34
+ </Section>
35
+
36
+ {spec.parameters && (
37
+ <Section title="Parameters Reference">
38
+ <PropertyList>
39
+ {spec.parameters.apiGroup && <Property label="API Group" value={spec.parameters.apiGroup} />}
40
+ <Property label="Kind" value={spec.parameters.kind} />
41
+ <Property label="Name" value={spec.parameters.name} />
42
+ {spec.parameters.namespace && <Property label="Namespace" value={spec.parameters.namespace} />}
43
+ {spec.parameters.scope && <Property label="Scope" value={spec.parameters.scope} />}
44
+ </PropertyList>
45
+ </Section>
46
+ )}
47
+ </>
48
+ )
49
+ }
@@ -0,0 +1,104 @@
1
+ import { Globe, Shield, Clock } from 'lucide-react'
2
+ import { Section, PropertyList, Property, AlertBanner, ResourceLink } from '../../ui/drawer-components'
3
+
4
+ interface IngressRendererProps {
5
+ data: any
6
+ onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
7
+ }
8
+
9
+ export function IngressRenderer({ data, onNavigate }: IngressRendererProps) {
10
+ const spec = data.spec || {}
11
+ const rules = spec.rules || []
12
+ const tls = spec.tls || []
13
+ const lbIngress = data.status?.loadBalancer?.ingress || []
14
+
15
+ // Check for issues
16
+ const hasNoAddress = lbIngress.length === 0
17
+ const hasNoClass = !spec.ingressClassName && !data.metadata?.annotations?.['kubernetes.io/ingress.class']
18
+ const hasNoRules = rules.length === 0
19
+
20
+ return (
21
+ <>
22
+ {/* No address warning */}
23
+ {hasNoAddress && (
24
+ <AlertBanner
25
+ variant="warning"
26
+ icon={Clock}
27
+ title="Address Not Assigned"
28
+ message={hasNoClass
29
+ ? 'No ingress class specified — an ingress controller may not pick up this resource.'
30
+ : 'Waiting for ingress controller to provision address. Check Events if this persists.'}
31
+ />
32
+ )}
33
+
34
+ {/* No rules warning */}
35
+ {hasNoRules && (
36
+ <AlertBanner
37
+ variant="info"
38
+ title="No Routing Rules"
39
+ message="This ingress has no rules defined. Traffic will not be routed."
40
+ />
41
+ )}
42
+
43
+ <Section title="Ingress" icon={Globe}>
44
+ <PropertyList>
45
+ <Property label="Class" value={spec.ingressClassName || data.metadata?.annotations?.['kubernetes.io/ingress.class']} />
46
+ {lbIngress.length > 0 && (
47
+ <Property label="Address" value={lbIngress[0].ip || lbIngress[0].hostname} />
48
+ )}
49
+ <Property label="TLS" value={tls.length > 0 ? `${tls.length} certificate(s)` : 'None'} />
50
+ </PropertyList>
51
+ </Section>
52
+
53
+ <Section title="Rules" defaultExpanded>
54
+ <div className="space-y-3">
55
+ {rules.map((rule: any, i: number) => (
56
+ <div key={i} className="bg-theme-elevated/30 rounded p-3">
57
+ <div className="flex items-center gap-2 mb-2">
58
+ {tls.some((t: any) => t.hosts?.includes(rule.host)) && (
59
+ <Shield className="w-3.5 h-3.5 text-green-400" />
60
+ )}
61
+ <span className="text-sm font-medium text-theme-text-primary">{rule.host || '*'}</span>
62
+ </div>
63
+ <div className="space-y-1">
64
+ {rule.http?.paths?.map((path: any) => (
65
+ <div key={path.path || '/'} className="text-xs text-theme-text-secondary flex items-center gap-2">
66
+ <span className="text-theme-text-tertiary">{path.pathType || 'Prefix'}:</span>
67
+ <span>{path.path || '/'}</span>
68
+ <span className="text-theme-text-tertiary">→</span>
69
+ {path.backend?.service?.name ? (
70
+ <ResourceLink
71
+ name={path.backend.service.name}
72
+ kind="services"
73
+ namespace={data.metadata?.namespace || ''}
74
+ label={<span className="text-blue-400">{path.backend.service.name}:{path.backend.service.port?.number || path.backend.service.port?.name}</span>}
75
+ onNavigate={onNavigate}
76
+ />
77
+ ) : (
78
+ <span className="text-blue-400">-</span>
79
+ )}
80
+ </div>
81
+ ))}
82
+ </div>
83
+ </div>
84
+ ))}
85
+ </div>
86
+ </Section>
87
+
88
+ {tls.length > 0 && (
89
+ <Section title="TLS" icon={Shield}>
90
+ <div className="space-y-2">
91
+ {tls.map((t: any) => (
92
+ <div key={t.secretName} className="text-sm">
93
+ <div className="text-theme-text-secondary">Secret: {t.secretName ? (
94
+ <ResourceLink name={t.secretName} kind="secrets" namespace={data.metadata?.namespace || ''} onNavigate={onNavigate} />
95
+ ) : '-'}</div>
96
+ <div className="text-xs text-theme-text-tertiary">Hosts: {t.hosts?.join(', ') || '*'}</div>
97
+ </div>
98
+ ))}
99
+ </div>
100
+ </Section>
101
+ )}
102
+ </>
103
+ )
104
+ }
@@ -0,0 +1,175 @@
1
+ import { Shield } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property, ConditionsSection, AlertBanner, KeyValueBadgeList } from '../../ui/drawer-components'
4
+ import {
5
+ getAuthorizationPolicyAction,
6
+ getAuthorizationPolicyRules,
7
+ getAuthorizationPolicySelector,
8
+ } from '../resource-utils-istio'
9
+
10
+ const actionColors: Record<string, string> = {
11
+ ALLOW: 'bg-green-500/20 text-green-400',
12
+ DENY: 'bg-red-500/20 text-red-400',
13
+ CUSTOM: 'bg-blue-500/20 text-blue-400',
14
+ AUDIT: 'bg-yellow-500/20 text-yellow-400',
15
+ }
16
+
17
+ interface IstioAuthorizationPolicyRendererProps {
18
+ data: any
19
+ }
20
+
21
+ export function IstioAuthorizationPolicyRenderer({ data }: IstioAuthorizationPolicyRendererProps) {
22
+ const action = getAuthorizationPolicyAction(data)
23
+ const rules = getAuthorizationPolicyRules(data)
24
+ const selector = getAuthorizationPolicySelector(data)
25
+ const hasSelector = Object.keys(selector).length > 0
26
+
27
+ // Special case: DENY with no rules = deny all
28
+ const isDenyAll = action === 'DENY' && rules.length === 0
29
+ // Special case: ALLOW with no rules = allow nothing (deny all)
30
+ const isAllowNothing = action === 'ALLOW' && rules.length === 0 && !data.spec?.rules
31
+
32
+ return (
33
+ <>
34
+ {isDenyAll && (
35
+ <AlertBanner
36
+ variant="error"
37
+ title="Deny All"
38
+ message="This policy denies all traffic to the target workload (DENY action with no rules)."
39
+ />
40
+ )}
41
+ {isAllowNothing && (
42
+ <AlertBanner
43
+ variant="warning"
44
+ title="Allow Nothing"
45
+ message="This policy has ALLOW action but no rules, which means no traffic is allowed."
46
+ />
47
+ )}
48
+
49
+ {/* Policy info */}
50
+ <Section title="Authorization Policy" icon={Shield} defaultExpanded>
51
+ <PropertyList>
52
+ <Property label="Action" value={
53
+ <span className={clsx(
54
+ 'px-2 py-0.5 rounded text-xs font-medium',
55
+ actionColors[action] || 'bg-gray-500/20 text-gray-400'
56
+ )}>
57
+ {action}
58
+ </span>
59
+ } />
60
+ <Property label="Scope" value={hasSelector ? 'Workload-scoped' : 'Namespace-wide'} />
61
+ <Property label="Rules" value={String(rules.length)} />
62
+ {data.spec?.provider?.name && (
63
+ <Property label="Provider" value={data.spec.provider.name} />
64
+ )}
65
+ </PropertyList>
66
+ </Section>
67
+
68
+ {/* Selector */}
69
+ {hasSelector && (
70
+ <Section title="Workload Selector" defaultExpanded>
71
+ <KeyValueBadgeList items={selector} />
72
+ </Section>
73
+ )}
74
+
75
+ {/* Rules */}
76
+ {rules.length > 0 && (
77
+ <Section title={`Rules (${rules.length})`} defaultExpanded>
78
+ <div className="space-y-3">
79
+ {rules.map((rule: any, i: number) => (
80
+ <div key={i} className="bg-theme-elevated/30 rounded p-3">
81
+ <div className="text-sm font-medium text-theme-text-primary mb-2">Rule {i + 1}</div>
82
+
83
+ {/* From (sources) */}
84
+ {rule.from && rule.from.length > 0 && (
85
+ <div className="mb-2">
86
+ <div className="text-[10px] font-medium text-theme-text-tertiary uppercase tracking-wider mb-1">From (Sources)</div>
87
+ {rule.from.map((f: any, fi: number) => (
88
+ <div key={fi} className="flex flex-wrap gap-1 mb-1">
89
+ {f.source?.principals?.map((p: string, pi: number) => (
90
+ <span key={`p-${pi}`} className="px-1.5 py-0.5 bg-blue-500/10 text-blue-400 rounded text-xs">
91
+ principal: {p}
92
+ </span>
93
+ ))}
94
+ {f.source?.namespaces?.map((n: string, ni: number) => (
95
+ <span key={`n-${ni}`} className="px-1.5 py-0.5 bg-purple-500/10 text-purple-400 rounded text-xs">
96
+ namespace: {n}
97
+ </span>
98
+ ))}
99
+ {f.source?.ipBlocks?.map((ip: string, ipi: number) => (
100
+ <span key={`ip-${ipi}`} className="px-1.5 py-0.5 bg-orange-500/10 text-orange-400 rounded text-xs">
101
+ IP: {ip}
102
+ </span>
103
+ ))}
104
+ {f.source?.requestPrincipals?.map((rp: string, rpi: number) => (
105
+ <span key={`rp-${rpi}`} className="px-1.5 py-0.5 bg-cyan-500/10 text-cyan-400 rounded text-xs">
106
+ reqPrincipal: {rp}
107
+ </span>
108
+ ))}
109
+ </div>
110
+ ))}
111
+ </div>
112
+ )}
113
+
114
+ {/* To (operations) */}
115
+ {rule.to && rule.to.length > 0 && (
116
+ <div className="mb-2">
117
+ <div className="text-[10px] font-medium text-theme-text-tertiary uppercase tracking-wider mb-1">To (Operations)</div>
118
+ {rule.to.map((t: any, ti: number) => (
119
+ <div key={ti} className="flex flex-wrap gap-1 mb-1">
120
+ {t.operation?.hosts?.map((h: string, hi: number) => (
121
+ <span key={`h-${hi}`} className="px-1.5 py-0.5 bg-blue-500/10 text-blue-400 rounded text-xs">
122
+ host: {h}
123
+ </span>
124
+ ))}
125
+ {t.operation?.ports?.map((p: string, pi: number) => (
126
+ <span key={`p-${pi}`} className="px-1.5 py-0.5 bg-orange-500/10 text-orange-400 rounded text-xs">
127
+ port: {p}
128
+ </span>
129
+ ))}
130
+ {t.operation?.methods?.map((m: string, mi: number) => (
131
+ <span key={`m-${mi}`} className="px-1.5 py-0.5 bg-green-500/10 text-green-400 rounded text-xs">
132
+ method: {m}
133
+ </span>
134
+ ))}
135
+ {t.operation?.paths?.map((p: string, pi: number) => (
136
+ <span key={`path-${pi}`} className="px-1.5 py-0.5 bg-purple-500/10 text-purple-400 rounded text-xs">
137
+ path: {p}
138
+ </span>
139
+ ))}
140
+ </div>
141
+ ))}
142
+ </div>
143
+ )}
144
+
145
+ {/* When (conditions) */}
146
+ {rule.when && rule.when.length > 0 && (
147
+ <div>
148
+ <div className="text-[10px] font-medium text-theme-text-tertiary uppercase tracking-wider mb-1">When (Conditions)</div>
149
+ {rule.when.map((w: any, wi: number) => (
150
+ <div key={wi} className="flex flex-wrap gap-1 mb-1 text-xs">
151
+ <span className="text-theme-text-secondary font-medium">{w.key}</span>
152
+ {w.values && (
153
+ <span className="text-theme-text-tertiary">
154
+ = [{w.values.join(', ')}]
155
+ </span>
156
+ )}
157
+ {w.notValues && (
158
+ <span className="text-red-400">
159
+ != [{w.notValues.join(', ')}]
160
+ </span>
161
+ )}
162
+ </div>
163
+ ))}
164
+ </div>
165
+ )}
166
+ </div>
167
+ ))}
168
+ </div>
169
+ </Section>
170
+ )}
171
+
172
+ <ConditionsSection conditions={data.status?.conditions || []} />
173
+ </>
174
+ )
175
+ }
@@ -0,0 +1,176 @@
1
+ import { Network } from 'lucide-react'
2
+ import { Section, PropertyList, Property, ConditionsSection, ResourceLink, KeyValueBadgeList } from '../../ui/drawer-components'
3
+ import {
4
+ getDestinationRuleHost,
5
+ getDestinationRuleSubsets,
6
+ getDestinationRuleTrafficPolicy,
7
+ getDestinationRuleLoadBalancer,
8
+ } from '../resource-utils-istio'
9
+
10
+ interface IstioDestinationRuleRendererProps {
11
+ data: any
12
+ onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
13
+ }
14
+
15
+ export function IstioDestinationRuleRenderer({ data, onNavigate }: IstioDestinationRuleRendererProps) {
16
+ const host = getDestinationRuleHost(data)
17
+ const subsets = getDestinationRuleSubsets(data)
18
+ const trafficPolicy = getDestinationRuleTrafficPolicy(data)
19
+ const loadBalancer = getDestinationRuleLoadBalancer(data)
20
+ const ns = data.metadata?.namespace || ''
21
+
22
+ // Parse host to create service link
23
+ const hostParts = host.split('.')
24
+ const svcName = hostParts[0]
25
+ const svcNs = hostParts.length >= 2 ? hostParts[1] : ns
26
+
27
+ return (
28
+ <>
29
+ {/* Host reference */}
30
+ <Section title="Destination" icon={Network} defaultExpanded>
31
+ <PropertyList>
32
+ <Property label="Host" value={
33
+ host !== '-' ? (
34
+ <ResourceLink
35
+ name={svcName}
36
+ kind="services"
37
+ namespace={svcNs}
38
+ label={host}
39
+ onNavigate={onNavigate}
40
+ />
41
+ ) : '-'
42
+ } />
43
+ <Property label="Load Balancer" value={loadBalancer} />
44
+ </PropertyList>
45
+ </Section>
46
+
47
+ {/* Traffic Policy */}
48
+ {trafficPolicy && (
49
+ <Section title="Traffic Policy" defaultExpanded>
50
+ <div className="space-y-3">
51
+ {/* Connection Pool */}
52
+ {trafficPolicy.connectionPool && (
53
+ <div>
54
+ <div className="text-xs font-medium text-theme-text-secondary uppercase tracking-wider mb-1">Connection Pool</div>
55
+ <PropertyList>
56
+ {trafficPolicy.connectionPool.tcp && (
57
+ <>
58
+ {trafficPolicy.connectionPool.tcp.maxConnections !== undefined && (
59
+ <Property label="Max TCP Connections" value={String(trafficPolicy.connectionPool.tcp.maxConnections)} />
60
+ )}
61
+ {trafficPolicy.connectionPool.tcp.connectTimeout && (
62
+ <Property label="Connect Timeout" value={trafficPolicy.connectionPool.tcp.connectTimeout} />
63
+ )}
64
+ </>
65
+ )}
66
+ {trafficPolicy.connectionPool.http && (
67
+ <>
68
+ {trafficPolicy.connectionPool.http.h2UpgradePolicy && (
69
+ <Property label="H2 Upgrade" value={trafficPolicy.connectionPool.http.h2UpgradePolicy} />
70
+ )}
71
+ {trafficPolicy.connectionPool.http.http1MaxPendingRequests !== undefined && (
72
+ <Property label="Max Pending Requests" value={String(trafficPolicy.connectionPool.http.http1MaxPendingRequests)} />
73
+ )}
74
+ {trafficPolicy.connectionPool.http.http2MaxRequests !== undefined && (
75
+ <Property label="Max Requests (H2)" value={String(trafficPolicy.connectionPool.http.http2MaxRequests)} />
76
+ )}
77
+ {trafficPolicy.connectionPool.http.maxRequestsPerConnection !== undefined && (
78
+ <Property label="Max Req/Connection" value={String(trafficPolicy.connectionPool.http.maxRequestsPerConnection)} />
79
+ )}
80
+ {trafficPolicy.connectionPool.http.maxRetries !== undefined && (
81
+ <Property label="Max Retries" value={String(trafficPolicy.connectionPool.http.maxRetries)} />
82
+ )}
83
+ </>
84
+ )}
85
+ </PropertyList>
86
+ </div>
87
+ )}
88
+
89
+ {/* Load Balancer */}
90
+ {trafficPolicy.loadBalancer && (
91
+ <div>
92
+ <div className="text-xs font-medium text-theme-text-secondary uppercase tracking-wider mb-1">Load Balancer</div>
93
+ <PropertyList>
94
+ {trafficPolicy.loadBalancer.simple && (
95
+ <Property label="Algorithm" value={trafficPolicy.loadBalancer.simple} />
96
+ )}
97
+ {trafficPolicy.loadBalancer.consistentHash && (
98
+ <Property label="Consistent Hash" value={
99
+ trafficPolicy.loadBalancer.consistentHash.httpHeaderName
100
+ ? `Header: ${trafficPolicy.loadBalancer.consistentHash.httpHeaderName}`
101
+ : trafficPolicy.loadBalancer.consistentHash.httpCookie
102
+ ? `Cookie: ${trafficPolicy.loadBalancer.consistentHash.httpCookie.name}`
103
+ : trafficPolicy.loadBalancer.consistentHash.useSourceIp
104
+ ? 'Source IP'
105
+ : 'Configured'
106
+ } />
107
+ )}
108
+ </PropertyList>
109
+ </div>
110
+ )}
111
+
112
+ {/* Outlier Detection */}
113
+ {trafficPolicy.outlierDetection && (
114
+ <div>
115
+ <div className="text-xs font-medium text-theme-text-secondary uppercase tracking-wider mb-1">Outlier Detection</div>
116
+ <PropertyList>
117
+ {trafficPolicy.outlierDetection.consecutiveErrors !== undefined && (
118
+ <Property label="Consecutive Errors" value={String(trafficPolicy.outlierDetection.consecutiveErrors)} />
119
+ )}
120
+ {trafficPolicy.outlierDetection.consecutive5xxErrors !== undefined && (
121
+ <Property label="Consecutive 5xx" value={String(trafficPolicy.outlierDetection.consecutive5xxErrors)} />
122
+ )}
123
+ {trafficPolicy.outlierDetection.interval && (
124
+ <Property label="Interval" value={trafficPolicy.outlierDetection.interval} />
125
+ )}
126
+ {trafficPolicy.outlierDetection.baseEjectionTime && (
127
+ <Property label="Ejection Time" value={trafficPolicy.outlierDetection.baseEjectionTime} />
128
+ )}
129
+ {trafficPolicy.outlierDetection.maxEjectionPercent !== undefined && (
130
+ <Property label="Max Ejection %" value={`${trafficPolicy.outlierDetection.maxEjectionPercent}%`} />
131
+ )}
132
+ </PropertyList>
133
+ </div>
134
+ )}
135
+
136
+ {/* TLS */}
137
+ {trafficPolicy.tls && (
138
+ <div>
139
+ <div className="text-xs font-medium text-theme-text-secondary uppercase tracking-wider mb-1">TLS</div>
140
+ <PropertyList>
141
+ <Property label="Mode" value={trafficPolicy.tls.mode || '-'} />
142
+ </PropertyList>
143
+ </div>
144
+ )}
145
+ </div>
146
+ </Section>
147
+ )}
148
+
149
+ {/* Subsets */}
150
+ {subsets.length > 0 && (
151
+ <Section title={`Subsets (${subsets.length})`} defaultExpanded>
152
+ <div className="space-y-3">
153
+ {subsets.map((subset, i) => (
154
+ <div key={i} className="bg-theme-elevated/30 rounded p-3">
155
+ <div className="text-sm font-medium text-theme-text-primary mb-2">{subset.name}</div>
156
+ {Object.keys(subset.labels).length > 0 && (
157
+ <div className="mb-2">
158
+ <div className="text-[10px] font-medium text-theme-text-tertiary uppercase tracking-wider mb-1">Labels</div>
159
+ <KeyValueBadgeList items={subset.labels} />
160
+ </div>
161
+ )}
162
+ {subset.trafficPolicy && (
163
+ <div className="text-xs text-theme-text-tertiary">
164
+ Has traffic policy override
165
+ </div>
166
+ )}
167
+ </div>
168
+ ))}
169
+ </div>
170
+ </Section>
171
+ )}
172
+
173
+ <ConditionsSection conditions={data.status?.conditions || []} />
174
+ </>
175
+ )
176
+ }
@@ -0,0 +1,125 @@
1
+ import { Globe, Lock } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property, ConditionsSection, AlertBanner, KeyValueBadgeList } from '../../ui/drawer-components'
4
+ import {
5
+ getIstioGatewayStatus,
6
+ getIstioGatewayServers,
7
+ getIstioGatewaySelector,
8
+ } from '../resource-utils-istio'
9
+
10
+ const protocolColors: Record<string, string> = {
11
+ HTTP: 'bg-blue-500/20 text-blue-400',
12
+ HTTPS: 'bg-green-500/20 text-green-400',
13
+ HTTP2: 'bg-blue-500/20 text-blue-400',
14
+ GRPC: 'bg-purple-500/20 text-purple-400',
15
+ TCP: 'bg-orange-500/20 text-orange-400',
16
+ TLS: 'bg-green-500/20 text-green-400',
17
+ MONGO: 'bg-emerald-500/20 text-emerald-400',
18
+ MYSQL: 'bg-cyan-500/20 text-cyan-400',
19
+ }
20
+
21
+ interface IstioGatewayRendererProps {
22
+ data: any
23
+ }
24
+
25
+ export function IstioGatewayRenderer({ data }: IstioGatewayRendererProps) {
26
+ const status = getIstioGatewayStatus(data)
27
+ const servers = getIstioGatewayServers(data)
28
+ const selector = getIstioGatewaySelector(data)
29
+
30
+ const hasNoServers = servers.length === 0
31
+
32
+ return (
33
+ <>
34
+ {hasNoServers && (
35
+ <AlertBanner
36
+ variant="error"
37
+ title="No Servers Configured"
38
+ message="This Istio Gateway has no server definitions."
39
+ />
40
+ )}
41
+
42
+ {/* Gateway info */}
43
+ <Section title="Gateway" icon={Globe} defaultExpanded>
44
+ <PropertyList>
45
+ <Property label="Status" value={
46
+ <span className={clsx('px-2 py-0.5 rounded text-xs font-medium', status.color)}>
47
+ {status.text}
48
+ </span>
49
+ } />
50
+ <Property label="Workload Selector" value={
51
+ Object.keys(selector).length > 0 ? (
52
+ <KeyValueBadgeList items={selector} />
53
+ ) : '-'
54
+ } />
55
+ </PropertyList>
56
+ </Section>
57
+
58
+ {/* Servers */}
59
+ {servers.length > 0 && (
60
+ <Section title={`Servers (${servers.length})`} defaultExpanded>
61
+ <div className="space-y-3">
62
+ {servers.map((server, i) => {
63
+ const isSecure = server.tls !== undefined
64
+ const protocol = server.port.protocol || 'TCP'
65
+
66
+ return (
67
+ <div key={i} className="bg-theme-elevated/30 rounded p-3">
68
+ <div className="flex items-center justify-between mb-2">
69
+ <div className="flex items-center gap-2">
70
+ {isSecure && <Lock className="w-3.5 h-3.5 text-green-400" />}
71
+ <span className="text-sm font-medium text-theme-text-primary">
72
+ {server.port.name || `Port ${server.port.number}`}
73
+ </span>
74
+ <span className={clsx(
75
+ 'px-1.5 py-0.5 rounded text-[10px] font-medium',
76
+ protocolColors[protocol] || 'bg-gray-500/20 text-gray-400'
77
+ )}>
78
+ {protocol}:{server.port.number}
79
+ </span>
80
+ </div>
81
+ </div>
82
+
83
+ {/* Hosts */}
84
+ <div className="space-y-1 text-xs text-theme-text-secondary">
85
+ <div>
86
+ <span className="text-theme-text-tertiary">Hosts: </span>
87
+ <span className="break-all">{server.hosts.join(', ')}</span>
88
+ </div>
89
+
90
+ {/* TLS settings */}
91
+ {server.tls && (
92
+ <>
93
+ <div>
94
+ <span className="text-theme-text-tertiary">TLS Mode: </span>
95
+ <span className={clsx(
96
+ 'px-1.5 py-0.5 rounded text-[10px] font-medium',
97
+ server.tls.mode === 'SIMPLE' || server.tls.mode === 'MUTUAL'
98
+ ? 'bg-green-500/20 text-green-400'
99
+ : server.tls.mode === 'PASSTHROUGH'
100
+ ? 'bg-blue-500/20 text-blue-400'
101
+ : 'bg-gray-500/20 text-gray-400'
102
+ )}>
103
+ {server.tls.mode || 'SIMPLE'}
104
+ </span>
105
+ </div>
106
+ {server.tls.credentialName && (
107
+ <div>
108
+ <span className="text-theme-text-tertiary">Credential: </span>
109
+ <span>{server.tls.credentialName}</span>
110
+ </div>
111
+ )}
112
+ </>
113
+ )}
114
+ </div>
115
+ </div>
116
+ )
117
+ })}
118
+ </div>
119
+ </Section>
120
+ )}
121
+
122
+ <ConditionsSection conditions={data.status?.conditions || []} />
123
+ </>
124
+ )
125
+ }