@skyhook-io/k8s-ui 1.4.1 → 1.4.3

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 (47) hide show
  1. package/package.json +1 -1
  2. package/src/components/resources/ResourcesSidebar.tsx +152 -57
  3. package/src/components/resources/ResourcesView.tsx +296 -14
  4. package/src/components/resources/renderers/AWSMachineRenderer.tsx +96 -0
  5. package/src/components/resources/renderers/AWSMachineTemplateRenderer.tsx +39 -0
  6. package/src/components/resources/renderers/AWSManagedClusterRenderer.tsx +49 -0
  7. package/src/components/resources/renderers/AWSManagedControlPlaneRenderer.tsx +174 -0
  8. package/src/components/resources/renderers/AWSManagedMachinePoolRenderer.tsx +89 -0
  9. package/src/components/resources/renderers/AzureMachineRenderer.tsx +41 -0
  10. package/src/components/resources/renderers/AzureManagedControlPlaneRenderer.tsx +71 -0
  11. package/src/components/resources/renderers/AzureManagedMachinePoolRenderer.tsx +106 -0
  12. package/src/components/resources/renderers/CAPIClusterClassRenderer.tsx +156 -0
  13. package/src/components/resources/renderers/CAPIClusterRenderer.tsx +240 -0
  14. package/src/components/resources/renderers/CAPIKubeadmConfigRenderer.tsx +97 -0
  15. package/src/components/resources/renderers/CAPIKubeadmControlPlaneRenderer.tsx +124 -0
  16. package/src/components/resources/renderers/CAPIMachineDeploymentRenderer.tsx +128 -0
  17. package/src/components/resources/renderers/CAPIMachineDrainRuleRenderer.tsx +38 -0
  18. package/src/components/resources/renderers/CAPIMachineHealthCheckRenderer.tsx +146 -0
  19. package/src/components/resources/renderers/CAPIMachinePoolRenderer.tsx +92 -0
  20. package/src/components/resources/renderers/CAPIMachineRenderer.tsx +170 -0
  21. package/src/components/resources/renderers/CAPIMachineSetRenderer.tsx +101 -0
  22. package/src/components/resources/renderers/GCPMachineRenderer.tsx +53 -0
  23. package/src/components/resources/renderers/GCPManagedControlPlaneRenderer.tsx +75 -0
  24. package/src/components/resources/renderers/GCPManagedMachinePoolRenderer.tsx +102 -0
  25. package/src/components/resources/renderers/aws-capi-cells.tsx +117 -0
  26. package/src/components/resources/renderers/azure-capi-cells.tsx +83 -0
  27. package/src/components/resources/renderers/capi-cells.tsx +158 -0
  28. package/src/components/resources/renderers/gcp-capi-cells.tsx +68 -0
  29. package/src/components/resources/renderers/index.ts +25 -0
  30. package/src/components/resources/resource-utils-aws-capi.ts +211 -0
  31. package/src/components/resources/resource-utils-azure-capi.ts +123 -0
  32. package/src/components/resources/resource-utils-capi.ts +305 -0
  33. package/src/components/resources/resource-utils-gcp-capi.ts +130 -0
  34. package/src/components/shared/EditableYamlView.tsx +38 -3
  35. package/src/components/shared/ResourceRendererDispatch.tsx +97 -2
  36. package/src/components/topology/K8sResourceNode.tsx +8 -0
  37. package/src/components/topology/TopologyControls.tsx +16 -0
  38. package/src/components/topology/TopologyFilterSidebar.tsx +15 -0
  39. package/src/components/topology/TopologyGraph.tsx +2 -1
  40. package/src/components/topology/layout.ts +11 -0
  41. package/src/components/topology/topology.css +29 -0
  42. package/src/components/ui/Badge.tsx +10 -0
  43. package/src/components/ui/Tooltip.tsx +16 -0
  44. package/src/components/ui/drawer-components.tsx +54 -17
  45. package/src/types/core.ts +18 -2
  46. package/src/utils/api-resources.ts +4 -0
  47. package/src/utils/resource-icons.ts +35 -0
@@ -139,6 +139,10 @@ import { VirtualServiceCell, DestinationRuleCell, IstioGatewayCell, ServiceEntry
139
139
  import { KnativeServiceCell, ConfigurationCell as KnativeConfigurationCell, RevisionCell as KnativeRevisionCell, RouteCell as KnativeRouteCell, BrokerCell, TriggerCell, EventTypeCell, PingSourceCell, ApiServerSourceCell, ContainerSourceCell, SinkBindingCell, ChannelCell, InMemoryChannelCell, SubscriptionCell, SequenceCell, ParallelCell, DomainMappingCell, ServerlessServiceCell, KnativeIngressCell, KnativeCertificateCell } from './renderers/knative-cells'
140
140
  import { IngressRouteCell, MiddlewareCell, TraefikServiceCell, ServersTransportCell, TLSOptionCell } from './renderers/traefik-cells'
141
141
  import { HTTPProxyCell } from './renderers/contour-cells'
142
+ import { CAPIClusterCell, CAPIMachineCell, CAPIMachineDeploymentCell, CAPIMachineSetCell, CAPIMachinePoolCell, CAPIKubeadmControlPlaneCell, CAPIClusterClassCell, CAPIMachineHealthCheckCell } from './renderers/capi-cells'
143
+ import { AWSManagedControlPlaneCell, AWSManagedMachinePoolCell, AWSMachineCell, AWSMachineTemplateCell, AWSManagedClusterCell } from './renderers/aws-capi-cells'
144
+ import { GCPManagedControlPlaneCell, GCPManagedMachinePoolCell, GCPMachineCell, GCPMachineTemplateCell, GCPManagedClusterCell } from './renderers/gcp-capi-cells'
145
+ import { AzureManagedControlPlaneCell, AzureManagedMachinePoolCell, AzureMachineCell, AzureMachineTemplateCell, AzureManagedClusterCell } from './renderers/azure-capi-cells'
142
146
  import { useRegisterShortcut, useRegisterShortcuts } from '../../hooks/useKeyboardShortcuts'
143
147
  import { ResourcesSidebar } from './ResourcesSidebar'
144
148
  import type { SelectedKindInfo } from './ResourcesSidebar'
@@ -264,7 +268,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
264
268
  { key: 'rules', label: 'Rules', width: 'min-w-56', hideOnMobile: true },
265
269
  { key: 'tls', label: 'TLS', width: 'w-14 shrink-0' },
266
270
  { key: 'address', label: 'Address', width: 'min-w-32', hideOnMobile: true },
267
- { key: 'age', label: 'Age', width: 'w-16 shrink-0' },
271
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
268
272
  ],
269
273
  nodes: [
270
274
  { key: 'name', label: 'Name' },
@@ -969,7 +973,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
969
973
  // ============================================================================
970
974
  // CLOUDNATIVEPG (CNPG) POSTGRESQL
971
975
  // ============================================================================
972
- clusters: [
976
+ cnpgclusters: [
973
977
  { key: 'name', label: 'Name' },
974
978
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
975
979
  { key: 'status', label: 'Status', width: 'w-28' },
@@ -1217,7 +1221,7 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1217
1221
  { key: 'routes', label: 'Routes', width: 'min-w-48', hideOnMobile: true },
1218
1222
  { key: 'tls', label: 'TLS', width: 'w-14 shrink-0' },
1219
1223
  { key: 'middlewares', label: 'MW', width: 'w-14 shrink-0', tooltip: 'Unique middleware count' },
1220
- { key: 'age', label: 'Age', width: 'w-16 shrink-0' },
1224
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1221
1225
  ],
1222
1226
  ingressroutetcps: [
1223
1227
  { key: 'name', label: 'Name', width: 'min-w-40' },
@@ -1227,58 +1231,249 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1227
1231
  { key: 'routes', label: 'Routes', width: 'min-w-48', hideOnMobile: true },
1228
1232
  { key: 'tls', label: 'TLS', width: 'w-14 shrink-0' },
1229
1233
  { key: 'middlewares', label: 'MW', width: 'w-14 shrink-0', tooltip: 'Unique middleware count' },
1230
- { key: 'age', label: 'Age', width: 'w-16 shrink-0' },
1234
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1231
1235
  ],
1232
1236
  ingressrouteudps: [
1233
1237
  { key: 'name', label: 'Name', width: 'min-w-40' },
1234
1238
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1235
1239
  { key: 'entrypoints', label: 'Entry Points', width: 'w-32 shrink-0' },
1236
1240
  { key: 'routes', label: 'Routes', width: 'min-w-48' },
1237
- { key: 'age', label: 'Age', width: 'w-16 shrink-0' },
1241
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1238
1242
  ],
1239
1243
  middlewares: [
1240
1244
  { key: 'name', label: 'Name', width: 'min-w-40' },
1241
1245
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1242
1246
  { key: 'type', label: 'Type', width: 'w-32 shrink-0' },
1243
- { key: 'age', label: 'Age', width: 'w-16 shrink-0' },
1247
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1244
1248
  ],
1245
1249
  middlewaretcps: [
1246
1250
  { key: 'name', label: 'Name', width: 'min-w-40' },
1247
1251
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1248
1252
  { key: 'type', label: 'Type', width: 'w-32 shrink-0' },
1249
- { key: 'age', label: 'Age', width: 'w-16 shrink-0' },
1253
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1250
1254
  ],
1251
1255
  traefikservices: [
1252
1256
  { key: 'name', label: 'Name', width: 'min-w-40' },
1253
1257
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1254
1258
  { key: 'type', label: 'Type', width: 'w-36 shrink-0' },
1255
1259
  { key: 'targets', label: 'Targets', width: 'min-w-48' },
1256
- { key: 'age', label: 'Age', width: 'w-16 shrink-0' },
1260
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1257
1261
  ],
1258
1262
  serverstransports: [
1259
1263
  { key: 'name', label: 'Name', width: 'min-w-40' },
1260
1264
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1261
1265
  { key: 'serverName', label: 'Server Name', width: 'w-40' },
1262
1266
  { key: 'insecure', label: 'Skip Verify', width: 'w-24 shrink-0' },
1263
- { key: 'age', label: 'Age', width: 'w-16 shrink-0' },
1267
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1264
1268
  ],
1265
1269
  serverstransporttcps: [
1266
1270
  { key: 'name', label: 'Name', width: 'min-w-40' },
1267
1271
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1268
1272
  { key: 'serverName', label: 'Server Name', width: 'w-40' },
1269
1273
  { key: 'insecure', label: 'Skip Verify', width: 'w-24 shrink-0' },
1270
- { key: 'age', label: 'Age', width: 'w-16 shrink-0' },
1274
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1271
1275
  ],
1272
1276
  tlsoptions: [
1273
1277
  { key: 'name', label: 'Name', width: 'min-w-40' },
1274
1278
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1275
1279
  { key: 'minVersion', label: 'Min TLS', width: 'w-24 shrink-0' },
1276
- { key: 'age', label: 'Age', width: 'w-16 shrink-0' },
1280
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1277
1281
  ],
1278
1282
  tlsstores: [
1279
1283
  { key: 'name', label: 'Name', width: 'min-w-40' },
1280
1284
  { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1281
- { key: 'age', label: 'Age', width: 'w-16 shrink-0' },
1285
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1286
+ ],
1287
+ // Cluster API (CAPI)
1288
+ capiclusters: [
1289
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1290
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1291
+ { key: 'provider', label: 'Provider', width: 'w-20 shrink-0' },
1292
+ { key: 'class', label: 'Class', width: 'w-32 shrink-0' },
1293
+ { key: 'cpReplicas', label: 'CP Ready', width: 'w-24 shrink-0', tooltip: 'Control plane replicas (ready/desired)' },
1294
+ { key: 'workerReplicas', label: 'Workers', width: 'w-20 shrink-0', tooltip: 'Worker replicas (ready/desired)' },
1295
+ { key: 'phase', label: 'Phase', width: 'w-28 shrink-0' },
1296
+ { key: 'version', label: 'Version', width: 'w-24 shrink-0' },
1297
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1298
+ ],
1299
+ machinedeployments: [
1300
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1301
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1302
+ { key: 'cluster', label: 'Cluster', width: 'w-32 shrink-0' },
1303
+ { key: 'ready', label: 'Ready', width: 'w-20 shrink-0', tooltip: 'Ready replicas / desired' },
1304
+ { key: 'phase', label: 'Phase', width: 'w-28 shrink-0' },
1305
+ { key: 'version', label: 'Version', width: 'w-24 shrink-0' },
1306
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1307
+ ],
1308
+ machines: [
1309
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1310
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1311
+ { key: 'cluster', label: 'Cluster', width: 'w-32 shrink-0' },
1312
+ { key: 'role', label: 'Role', width: 'w-28 shrink-0' },
1313
+ { key: 'phase', label: 'Phase', width: 'w-28 shrink-0' },
1314
+ { key: 'node', label: 'Node', width: 'w-32 shrink-0' },
1315
+ { key: 'version', label: 'Version', width: 'w-24 shrink-0' },
1316
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1317
+ ],
1318
+ machinesets: [
1319
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1320
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1321
+ { key: 'cluster', label: 'Cluster', width: 'w-32 shrink-0' },
1322
+ { key: 'ready', label: 'Ready', width: 'w-20 shrink-0' },
1323
+ { key: 'phase', label: 'Phase', width: 'w-28 shrink-0' },
1324
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1325
+ ],
1326
+ machinepools: [
1327
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1328
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1329
+ { key: 'cluster', label: 'Cluster', width: 'w-32 shrink-0' },
1330
+ { key: 'ready', label: 'Ready', width: 'w-20 shrink-0' },
1331
+ { key: 'phase', label: 'Phase', width: 'w-28 shrink-0' },
1332
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1333
+ ],
1334
+ kubeadmcontrolplanes: [
1335
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1336
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1337
+ { key: 'cluster', label: 'Cluster', width: 'w-32 shrink-0' },
1338
+ { key: 'ready', label: 'Ready', width: 'w-20 shrink-0' },
1339
+ { key: 'initialized', label: 'Initialized', width: 'w-24 shrink-0' },
1340
+ { key: 'version', label: 'Version', width: 'w-24 shrink-0' },
1341
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1342
+ ],
1343
+ clusterclasses: [
1344
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1345
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1346
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1347
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1348
+ ],
1349
+ machinehealthchecks: [
1350
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1351
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1352
+ { key: 'cluster', label: 'Cluster', width: 'w-32 shrink-0' },
1353
+ { key: 'healthy', label: 'Healthy', width: 'w-20 shrink-0', tooltip: 'Healthy machines / expected' },
1354
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1355
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1356
+ ],
1357
+ // AWS CAPI Infrastructure Provider
1358
+ awsmanagedcontrolplanes: [
1359
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1360
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1361
+ { key: 'eksCluster', label: 'EKS Cluster', width: 'w-36 shrink-0' },
1362
+ { key: 'region', label: 'Region', width: 'w-28 shrink-0' },
1363
+ { key: 'version', label: 'Version', width: 'w-24 shrink-0' },
1364
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1365
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1366
+ ],
1367
+ awsmanagedmachinepools: [
1368
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1369
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1370
+ { key: 'instanceType', label: 'Instance', width: 'w-28 shrink-0' },
1371
+ { key: 'replicas', label: 'Ready', width: 'w-20 shrink-0', tooltip: 'Ready replicas' },
1372
+ { key: 'capacityType', label: 'Capacity', width: 'w-28 shrink-0' },
1373
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1374
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1375
+ ],
1376
+ awsmachines: [
1377
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1378
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1379
+ { key: 'instanceType', label: 'Instance', width: 'w-28 shrink-0' },
1380
+ { key: 'instanceState', label: 'State', width: 'w-24 shrink-0' },
1381
+ { key: 'instanceID', label: 'Instance ID', width: 'w-40 shrink-0' },
1382
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1383
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1384
+ ],
1385
+ awsmachinetemplates: [
1386
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1387
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1388
+ { key: 'instanceType', label: 'Instance', width: 'w-28 shrink-0' },
1389
+ { key: 'capacity', label: 'Capacity', width: 'w-32 shrink-0', tooltip: 'Computed CPU/memory' },
1390
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1391
+ ],
1392
+ awsmanagedclusters: [
1393
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1394
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1395
+ { key: 'endpoint', label: 'Endpoint', width: 'min-w-44' },
1396
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1397
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1398
+ ],
1399
+ // GCP CAPI Infrastructure Provider
1400
+ gcpmanagedcontrolplanes: [
1401
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1402
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1403
+ { key: 'gkeCluster', label: 'GKE Cluster', width: 'w-36 shrink-0' },
1404
+ { key: 'project', label: 'Project', width: 'w-36 shrink-0' },
1405
+ { key: 'location', label: 'Location', width: 'w-28 shrink-0' },
1406
+ { key: 'version', label: 'Version', width: 'w-24 shrink-0' },
1407
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1408
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1409
+ ],
1410
+ gcpmanagedmachinepools: [
1411
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1412
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1413
+ { key: 'machineType', label: 'Machine Type', width: 'w-32 shrink-0' },
1414
+ { key: 'replicas', label: 'Replicas', width: 'w-20 shrink-0' },
1415
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1416
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1417
+ ],
1418
+ gcpmachines: [
1419
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1420
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1421
+ { key: 'instanceType', label: 'Instance', width: 'w-28 shrink-0' },
1422
+ { key: 'zone', label: 'Zone', width: 'w-28 shrink-0' },
1423
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1424
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1425
+ ],
1426
+ gcpmachinetemplates: [
1427
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1428
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1429
+ { key: 'instanceType', label: 'Instance', width: 'w-28 shrink-0' },
1430
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1431
+ ],
1432
+ gcpmanagedclusters: [
1433
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1434
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1435
+ { key: 'project', label: 'Project', width: 'w-36 shrink-0' },
1436
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1437
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1438
+ ],
1439
+ // Azure CAPI Infrastructure Provider
1440
+ azuremanagedcontrolplanes: [
1441
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1442
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1443
+ { key: 'location', label: 'Location', width: 'w-28 shrink-0' },
1444
+ { key: 'resourceGroup', label: 'Resource Group', width: 'w-40 shrink-0' },
1445
+ { key: 'version', label: 'Version', width: 'w-24 shrink-0' },
1446
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1447
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1448
+ ],
1449
+ azuremanagedmachinepools: [
1450
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1451
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1452
+ { key: 'sku', label: 'VM Size', width: 'w-32 shrink-0' },
1453
+ { key: 'mode', label: 'Mode', width: 'w-20 shrink-0' },
1454
+ { key: 'replicas', label: 'Replicas', width: 'w-20 shrink-0' },
1455
+ { key: 'priority', label: 'Priority', width: 'w-24 shrink-0' },
1456
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1457
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1458
+ ],
1459
+ azuremachines: [
1460
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1461
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1462
+ { key: 'vmSize', label: 'VM Size', width: 'w-32 shrink-0' },
1463
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1464
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1465
+ ],
1466
+ azuremachinetemplates: [
1467
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1468
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1469
+ { key: 'vmSize', label: 'VM Size', width: 'w-32 shrink-0' },
1470
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1471
+ ],
1472
+ azuremanagedclusters: [
1473
+ { key: 'name', label: 'Name', width: 'min-w-40' },
1474
+ { key: 'namespace', label: 'Namespace', width: 'w-36 shrink-0' },
1475
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1476
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1282
1477
  ],
1283
1478
  // Contour
1284
1479
  httpproxies: [
@@ -1288,13 +1483,14 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1288
1483
  { key: 'routes', label: 'Routes', width: 'w-20 shrink-0' },
1289
1484
  { key: 'includes', label: 'Includes', width: 'w-24 shrink-0' },
1290
1485
  { key: 'tls', label: 'TLS', width: 'w-14 shrink-0' },
1291
- { key: 'status', label: 'Status', width: 'w-24 shrink-0' },
1292
- { key: 'age', label: 'Age', width: 'w-16 shrink-0' },
1486
+ { key: 'status', label: 'Status', width: 'w-28 shrink-0' },
1487
+ { key: 'age', label: 'Age', width: 'w-20 shrink-0' },
1293
1488
  ],
1294
1489
  }
1295
1490
 
1296
1491
  // Map (plural, group) → KNOWN_COLUMNS key for kinds that collide with core K8s
1297
1492
  const GROUP_QUALIFIED_COLUMN_KEYS: Record<string, Record<string, string>> = {
1493
+ clusters: { 'postgresql.cnpg.io': 'cnpgclusters', 'cluster.x-k8s.io': 'capiclusters' },
1298
1494
  services: { 'serving.knative.dev': 'knativeservices' },
1299
1495
  configurations: { 'serving.knative.dev': 'knativeconfigurations' },
1300
1496
  revisions: { 'serving.knative.dev': 'knativerevisions' },
@@ -2938,6 +3134,11 @@ export function ResourcesView({
2938
3134
  pinned={pinned}
2939
3135
  togglePin={togglePin}
2940
3136
  isPinned={isPinned}
3137
+ onKindNavigated={() => {
3138
+ // After selecting a kind via keyboard, move focus to the table search
3139
+ // so the user can immediately filter within the selected kind.
3140
+ setTimeout(() => searchInputRef.current?.focus(), 50)
3141
+ }}
2941
3142
  />
2942
3143
  )}
2943
3144
 
@@ -2953,6 +3154,30 @@ export function ResourcesView({
2953
3154
  placeholder="Search... (press /)"
2954
3155
  value={searchTerm}
2955
3156
  onChange={(e) => setSearchTerm(e.target.value)}
3157
+ onKeyDown={(e) => {
3158
+ if (e.key === 'ArrowDown') {
3159
+ // Hand off to the table's keyboard navigation — blur the input
3160
+ // so the registered ArrowDown/j/k shortcuts take over, and
3161
+ // highlight the first row.
3162
+ e.preventDefault()
3163
+ searchInputRef.current?.blur()
3164
+ setHighlightedIndex(0)
3165
+ } else if (e.key === 'Enter' && filteredResourceCountRef.current > 0) {
3166
+ // Select the first (or currently highlighted) resource
3167
+ e.preventDefault()
3168
+ searchInputRef.current?.blur()
3169
+ if (highlightedIndex < 0) setHighlightedIndex(0)
3170
+ // Defer to next frame so the highlight renders before we open
3171
+ requestAnimationFrame(() => {
3172
+ const res = highlightedResourceRef.current ?? filteredResources[0]
3173
+ if (res?.metadata?.name) {
3174
+ onResourceClick?.({ kind: selectedKind.name, namespace: res.metadata.namespace || '', name: res.metadata.name, group: selectedKind.group })
3175
+ }
3176
+ })
3177
+ } else if (e.key === 'Escape') {
3178
+ searchInputRef.current?.blur()
3179
+ }
3180
+ }}
2956
3181
  className="w-full max-w-md pl-10 pr-4 py-2 bg-theme-elevated border border-theme-border-light rounded-lg text-sm text-theme-text-primary placeholder-theme-text-disabled focus:outline-none focus:ring-2 focus:ring-skyhook-500"
2957
3182
  />
2958
3183
  </div>
@@ -3775,6 +4000,7 @@ function CellContent({ resource, kind, column, group, majorityNodeMinorVersion }
3775
4000
  case 'backupstoragelocations':
3776
4001
  return <BackupStorageLocationCell resource={resource} column={column} />
3777
4002
  // CloudNativePG
4003
+ case 'cnpgclusters':
3778
4004
  case 'clusters':
3779
4005
  return <CNPGClusterCell resource={resource} column={column} />
3780
4006
  case 'scheduledbackups':
@@ -3856,6 +4082,62 @@ function CellContent({ resource, kind, column, group, majorityNodeMinorVersion }
3856
4082
  // Contour
3857
4083
  case 'httpproxies':
3858
4084
  return <HTTPProxyCell resource={resource} column={column} />
4085
+ // Cluster API (CAPI)
4086
+ case 'capiclusters':
4087
+ return <CAPIClusterCell resource={resource} column={column} />
4088
+ case 'machines':
4089
+ return <CAPIMachineCell resource={resource} column={column} />
4090
+ case 'machinedeployments':
4091
+ return <CAPIMachineDeploymentCell resource={resource} column={column} />
4092
+ case 'machinesets':
4093
+ return <CAPIMachineSetCell resource={resource} column={column} />
4094
+ case 'machinepools':
4095
+ return <CAPIMachinePoolCell resource={resource} column={column} />
4096
+ case 'kubeadmcontrolplanes':
4097
+ return <CAPIKubeadmControlPlaneCell resource={resource} column={column} />
4098
+ case 'clusterclasses':
4099
+ return <CAPIClusterClassCell resource={resource} column={column} />
4100
+ case 'machinehealthchecks':
4101
+ return <CAPIMachineHealthCheckCell resource={resource} column={column} />
4102
+ case 'machinedrainrules':
4103
+ case 'kubeadmconfigs':
4104
+ case 'kubeadmconfigtemplates':
4105
+ case 'kubeadmcontrolplanetemplates':
4106
+ // These use default columns — no custom cell renderer needed
4107
+ return <GenericCell resource={resource} column={column} />
4108
+ // AWS CAPI Infrastructure Provider
4109
+ case 'awsmanagedcontrolplanes':
4110
+ return <AWSManagedControlPlaneCell resource={resource} column={column} />
4111
+ case 'awsmanagedmachinepools':
4112
+ return <AWSManagedMachinePoolCell resource={resource} column={column} />
4113
+ case 'awsmachines':
4114
+ return <AWSMachineCell resource={resource} column={column} />
4115
+ case 'awsmachinetemplates':
4116
+ return <AWSMachineTemplateCell resource={resource} column={column} />
4117
+ case 'awsmanagedclusters':
4118
+ return <AWSManagedClusterCell resource={resource} column={column} />
4119
+ // GCP CAPI Infrastructure
4120
+ case 'gcpmanagedcontrolplanes':
4121
+ return <GCPManagedControlPlaneCell resource={resource} column={column} />
4122
+ case 'gcpmanagedmachinepools':
4123
+ return <GCPManagedMachinePoolCell resource={resource} column={column} />
4124
+ case 'gcpmachines':
4125
+ return <GCPMachineCell resource={resource} column={column} />
4126
+ case 'gcpmachinetemplates':
4127
+ return <GCPMachineTemplateCell resource={resource} column={column} />
4128
+ case 'gcpmanagedclusters':
4129
+ return <GCPManagedClusterCell resource={resource} column={column} />
4130
+ // Azure CAPI Infrastructure
4131
+ case 'azuremanagedcontrolplanes':
4132
+ return <AzureManagedControlPlaneCell resource={resource} column={column} />
4133
+ case 'azuremanagedmachinepools':
4134
+ return <AzureManagedMachinePoolCell resource={resource} column={column} />
4135
+ case 'azuremachines':
4136
+ return <AzureMachineCell resource={resource} column={column} />
4137
+ case 'azuremachinetemplates':
4138
+ return <AzureMachineTemplateCell resource={resource} column={column} />
4139
+ case 'azuremanagedclusters':
4140
+ return <AzureManagedClusterCell resource={resource} column={column} />
3859
4141
  default:
3860
4142
  // Generic cell for CRDs and unknown resources
3861
4143
  return <GenericCell resource={resource} column={column} />
@@ -0,0 +1,96 @@
1
+ import { Cpu, Network, Cloud } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property, ConditionsSection, AlertBanner } from '../../ui/drawer-components'
4
+ import { getCAPIConditions } from '../resource-utils-capi'
5
+ import { getAWSMachineStatus, getAWSMachineInstanceType, getAWSMachineInstanceState, getAWSMachineInstanceID } from '../resource-utils-aws-capi'
6
+
7
+ interface Props {
8
+ data: any
9
+ onNavigate?: (ref: { kind: string; namespace: string; name: string; group?: string }) => void
10
+ }
11
+
12
+ export function AWSMachineRenderer({ data }: Props) {
13
+ const spec = data.spec || {}
14
+ const status = data.status || {}
15
+ const conditions = getCAPIConditions(data)
16
+
17
+ const machineStatus = getAWSMachineStatus(data)
18
+ const isFailed = machineStatus.level === 'unhealthy'
19
+ const readyCond = conditions.find((c: any) => c.type === 'Ready')
20
+
21
+ const instanceState = getAWSMachineInstanceState(data)
22
+ const addresses = status.addresses || []
23
+
24
+ return (
25
+ <>
26
+ {isFailed && (
27
+ <AlertBanner
28
+ variant="error"
29
+ title="AWS Machine Not Ready"
30
+ message={readyCond?.message || 'AWSMachine is not ready.'}
31
+ />
32
+ )}
33
+
34
+ <Section title="Instance" icon={Cloud}>
35
+ <PropertyList>
36
+ <Property label="Instance Type" value={getAWSMachineInstanceType(data)} />
37
+ <Property label="Instance ID" value={
38
+ <span className="font-mono text-[11px]">{getAWSMachineInstanceID(data)}</span>
39
+ } />
40
+ <Property label="State" value={
41
+ <span className={clsx('badge badge-sm', instanceState === 'running'
42
+ ? 'status-healthy'
43
+ : instanceState === 'pending' || instanceState === 'stopping' || instanceState === 'stopped'
44
+ ? 'status-degraded'
45
+ : instanceState === 'terminated' || instanceState === 'shutting-down'
46
+ ? 'status-unhealthy'
47
+ : 'status-neutral'
48
+ )}>{instanceState}</span>
49
+ } />
50
+ {spec.providerID && spec.providerID !== '-' && (
51
+ <Property label="Provider ID" value={
52
+ <span className="font-mono text-[10px] break-all">{spec.providerID}</span>
53
+ } />
54
+ )}
55
+ </PropertyList>
56
+ </Section>
57
+
58
+ <Section title="Configuration" icon={Cpu}>
59
+ <PropertyList>
60
+ {spec.iamInstanceProfile && <Property label="IAM Profile" value={spec.iamInstanceProfile} />}
61
+ {spec.sshKeyName && <Property label="SSH Key" value={spec.sshKeyName} />}
62
+ {spec.subnet?.id && (
63
+ <Property label="Subnet" value={<span className="font-mono text-[11px]">{spec.subnet.id}</span>} />
64
+ )}
65
+ {spec.cloudInit?.secureSecretsBackend && (
66
+ <Property label="Secrets Backend" value={spec.cloudInit.secureSecretsBackend} />
67
+ )}
68
+ </PropertyList>
69
+ </Section>
70
+
71
+ {/* Addresses */}
72
+ {addresses.length > 0 && (
73
+ <Section title="Addresses" icon={Network}>
74
+ <table className="w-full text-xs">
75
+ <thead>
76
+ <tr className="text-theme-text-tertiary">
77
+ <th className="text-left font-medium py-1">Type</th>
78
+ <th className="text-left font-medium py-1">Address</th>
79
+ </tr>
80
+ </thead>
81
+ <tbody>
82
+ {addresses.map((addr: any, i: number) => (
83
+ <tr key={i} className="border-t border-theme-border">
84
+ <td className="py-1 text-theme-text-secondary">{addr.type}</td>
85
+ <td className="py-1 text-theme-text-secondary font-mono">{addr.address}</td>
86
+ </tr>
87
+ ))}
88
+ </tbody>
89
+ </table>
90
+ </Section>
91
+ )}
92
+
93
+ <ConditionsSection conditions={conditions} />
94
+ </>
95
+ )
96
+ }
@@ -0,0 +1,39 @@
1
+ import { Cpu, Server } from 'lucide-react'
2
+ import { Section, PropertyList, Property } from '../../ui/drawer-components'
3
+ import { getAWSMTInstanceType } from '../resource-utils-aws-capi'
4
+
5
+ interface Props {
6
+ data: any
7
+ }
8
+
9
+ export function AWSMachineTemplateRenderer({ data }: Props) {
10
+ const templateSpec = data.spec?.template?.spec || {}
11
+ const status = data.status || {}
12
+
13
+ return (
14
+ <>
15
+ <Section title="Template Spec" icon={Cpu}>
16
+ <PropertyList>
17
+ <Property label="Instance Type" value={getAWSMTInstanceType(data)} />
18
+ {templateSpec.subnet?.id && (
19
+ <Property label="Subnet" value={<span className="font-mono text-[11px]">{templateSpec.subnet.id}</span>} />
20
+ )}
21
+ {templateSpec.iamInstanceProfile && <Property label="IAM Profile" value={templateSpec.iamInstanceProfile} />}
22
+ {templateSpec.sshKeyName && <Property label="SSH Key" value={templateSpec.sshKeyName} />}
23
+ </PropertyList>
24
+ </Section>
25
+
26
+ {/* Resolved capacity from status */}
27
+ {(status.capacity || status.nodeInfo) && (
28
+ <Section title="Resolved Info" icon={Server}>
29
+ <PropertyList>
30
+ {status.capacity?.cpu && <Property label="CPU" value={status.capacity.cpu} />}
31
+ {status.capacity?.memory && <Property label="Memory" value={status.capacity.memory} />}
32
+ {status.nodeInfo?.architecture && <Property label="Architecture" value={status.nodeInfo.architecture} />}
33
+ {status.nodeInfo?.operatingSystem && <Property label="OS" value={status.nodeInfo.operatingSystem} />}
34
+ </PropertyList>
35
+ </Section>
36
+ )}
37
+ </>
38
+ )
39
+ }
@@ -0,0 +1,49 @@
1
+ import { Globe, Server } from 'lucide-react'
2
+ import { Section, PropertyList, Property, ConditionsSection, AlertBanner } from '../../ui/drawer-components'
3
+ import { getCAPIConditions } from '../resource-utils-capi'
4
+ import { getAWSManagedClusterStatus, getAWSManagedClusterEndpoint, getAWSManagedClusterFailureDomains } from '../resource-utils-aws-capi'
5
+
6
+ interface Props {
7
+ data: any
8
+ }
9
+
10
+ export function AWSManagedClusterRenderer({ data }: Props) {
11
+ const conditions = getCAPIConditions(data)
12
+ const clusterStatus = getAWSManagedClusterStatus(data)
13
+ const isFailed = clusterStatus.level === 'unhealthy'
14
+ const readyCond = conditions.find((c: any) => c.type === 'Ready')
15
+ const failureDomains = getAWSManagedClusterFailureDomains(data)
16
+ const endpoint = getAWSManagedClusterEndpoint(data)
17
+
18
+ return (
19
+ <>
20
+ {isFailed && (
21
+ <AlertBanner
22
+ variant="error"
23
+ title="Managed Cluster Not Ready"
24
+ message={readyCond?.message || 'AWSManagedCluster is not ready.'}
25
+ />
26
+ )}
27
+
28
+ <Section title="Overview" icon={Globe}>
29
+ <PropertyList>
30
+ {endpoint !== '-' && (
31
+ <Property label="Endpoint" value={<span className="font-mono text-[10px] break-all">{endpoint}</span>} />
32
+ )}
33
+ </PropertyList>
34
+ </Section>
35
+
36
+ {failureDomains.length > 0 && (
37
+ <Section title="Failure Domains" icon={Server}>
38
+ <div className="flex flex-wrap gap-1">
39
+ {failureDomains.map((az) => (
40
+ <span key={az} className="badge badge-sm bg-theme-elevated text-theme-text-secondary border-theme-border">{az}</span>
41
+ ))}
42
+ </div>
43
+ </Section>
44
+ )}
45
+
46
+ <ConditionsSection conditions={conditions} />
47
+ </>
48
+ )
49
+ }