@backstage/plugin-kubernetes 0.4.21 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -4,7 +4,7 @@ import React__default, { Fragment, useState, useEffect, useContext } from 'react
4
4
  import { useEntity } from '@backstage/plugin-catalog-react';
5
5
  import { Routes, Route } from 'react-router-dom';
6
6
  import { Typography, Chip, Grid, makeStyles, createStyles, Button, Drawer, IconButton, FormControlLabel, Switch, Accordion, AccordionSummary, AccordionDetails, Divider, Stepper, Step, StepLabel } from '@material-ui/core';
7
- import { WarningPanel, InfoCard, Table, SubvalueCell, StatusError, StatusOK, StatusAborted, Button as Button$1, CodeSnippet, StructuredMetadataTable, Page, Content, Progress, MissingAnnotationEmptyState } from '@backstage/core-components';
7
+ import { WarningPanel, InfoCard, Table, StatusOK, SubvalueCell, StatusError, StatusAborted, Button as Button$1, CodeSnippet, StructuredMetadataTable, StatusPending, Page, Content, Progress, MissingAnnotationEmptyState } from '@backstage/core-components';
8
8
  import EmptyStateImage from './assets/emptystate.svg';
9
9
  import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
10
10
  import Close from '@material-ui/icons/Close';
@@ -12,6 +12,7 @@ import OpenInNewIcon from '@material-ui/icons/OpenInNew';
12
12
  import { withStyles } from '@material-ui/core/styles';
13
13
  import jsYaml from 'js-yaml';
14
14
  import { useInterval } from 'react-use';
15
+ import cronstrue from 'cronstrue';
15
16
  import lodash from 'lodash';
16
17
  import PauseIcon from '@material-ui/icons/Pause';
17
18
  import ErrorOutlineIcon from '@material-ui/icons/ErrorOutline';
@@ -44,7 +45,7 @@ class KubernetesBackendClient {
44
45
  method: "POST",
45
46
  headers: {
46
47
  "Content-Type": "application/json",
47
- ...idToken && {Authorization: `Bearer ${idToken}`}
48
+ ...idToken && { Authorization: `Bearer ${idToken}` }
48
49
  },
49
50
  body: JSON.stringify(requestBody)
50
51
  });
@@ -59,7 +60,7 @@ class KubernetesBackendClient {
59
60
  const response = await fetch(url, {
60
61
  method: "GET",
61
62
  headers: {
62
- ...idToken && {Authorization: `Bearer ${idToken}`}
63
+ ...idToken && { Authorization: `Bearer ${idToken}` }
63
64
  }
64
65
  });
65
66
  return (await this.handleResponse(response)).items;
@@ -67,13 +68,11 @@ class KubernetesBackendClient {
67
68
  }
68
69
 
69
70
  const kubernetesApiRef = createApiRef({
70
- id: "plugin.kubernetes.service",
71
- description: "Used by the Kubernetes plugin to make requests to accompanying backend"
71
+ id: "plugin.kubernetes.service"
72
72
  });
73
73
 
74
74
  const kubernetesAuthProvidersApiRef = createApiRef({
75
- id: "plugin.kubernetes-auth-providers.service",
76
- description: "Used by the Kubernetes plugin to fetch KubernetesAuthProviders"
75
+ id: "plugin.kubernetes-auth-providers.service"
77
76
  });
78
77
 
79
78
  class GoogleKubernetesAuthProvider {
@@ -85,7 +84,7 @@ class GoogleKubernetesAuthProvider {
85
84
  if ("auth" in requestBody) {
86
85
  requestBody.auth.google = googleAuthToken;
87
86
  } else {
88
- requestBody.auth = {google: googleAuthToken};
87
+ requestBody.auth = { google: googleAuthToken };
89
88
  }
90
89
  return requestBody;
91
90
  }
@@ -105,7 +104,7 @@ class AwsKubernetesAuthProvider {
105
104
 
106
105
  class KubernetesAuthProviders {
107
106
  constructor(options) {
108
- this.kubernetesAuthProviderMap = new Map();
107
+ this.kubernetesAuthProviderMap = /* @__PURE__ */ new Map();
109
108
  this.kubernetesAuthProviderMap.set("google", new GoogleKubernetesAuthProvider(options.googleAuthApi));
110
109
  this.kubernetesAuthProviderMap.set("serviceAccount", new ServiceAccountKubernetesAuthProvider());
111
110
  this.kubernetesAuthProviderMap.set("aws", new AwsKubernetesAuthProvider());
@@ -131,13 +130,13 @@ const kubernetesPlugin = createPlugin({
131
130
  discoveryApi: discoveryApiRef,
132
131
  identityApi: identityApiRef
133
132
  },
134
- factory: ({discoveryApi, identityApi}) => new KubernetesBackendClient({discoveryApi, identityApi})
133
+ factory: ({ discoveryApi, identityApi }) => new KubernetesBackendClient({ discoveryApi, identityApi })
135
134
  }),
136
135
  createApiFactory({
137
136
  api: kubernetesAuthProvidersApiRef,
138
- deps: {googleAuthApi: googleAuthApiRef},
139
- factory: ({googleAuthApi}) => {
140
- return new KubernetesAuthProviders({googleAuthApi});
137
+ deps: { googleAuthApi: googleAuthApiRef },
138
+ factory: ({ googleAuthApi }) => {
139
+ return new KubernetesAuthProviders({ googleAuthApi });
141
140
  }
142
141
  })
143
142
  ],
@@ -176,7 +175,7 @@ const ErrorPanel$1 = ({
176
175
  variant: "body2"
177
176
  }, "Errors: ", errorMessage));
178
177
 
179
- const columns$1 = [
178
+ const columns = [
180
179
  {
181
180
  title: "cluster",
182
181
  width: "15%",
@@ -240,15 +239,15 @@ const ErrorEmptyState = () => {
240
239
  "data-testid": "emptyStateImg"
241
240
  })));
242
241
  };
243
- const ErrorReporting = ({detectedErrors}) => {
242
+ const ErrorReporting = ({ detectedErrors }) => {
244
243
  const errors = Array.from(detectedErrors.values()).flat().sort(sortBySeverity);
245
244
  return /* @__PURE__ */ React.createElement(React.Fragment, null, errors.length === 0 ? /* @__PURE__ */ React.createElement(InfoCard, {
246
245
  title: "Error Reporting"
247
246
  }, /* @__PURE__ */ React.createElement(ErrorEmptyState, null)) : /* @__PURE__ */ React.createElement(Table, {
248
247
  title: "Error Reporting",
249
248
  data: errors,
250
- columns: columns$1,
251
- options: {paging: true, search: false}
249
+ columns,
250
+ options: { paging: true, search: false }
252
251
  }));
253
252
  };
254
253
 
@@ -276,6 +275,12 @@ const groupResponses = (fetchResponse) => {
276
275
  case "ingresses":
277
276
  prev.ingresses.push(...next.resources);
278
277
  break;
278
+ case "jobs":
279
+ prev.jobs.push(...next.resources);
280
+ break;
281
+ case "cronjobs":
282
+ prev.cronJobs.push(...next.resources);
283
+ break;
279
284
  case "customresources":
280
285
  prev.customResources.push(...next.resources);
281
286
  break;
@@ -289,6 +294,8 @@ const groupResponses = (fetchResponse) => {
289
294
  configMaps: [],
290
295
  horizontalPodAutoscalers: [],
291
296
  ingresses: [],
297
+ jobs: [],
298
+ cronJobs: [],
292
299
  customResources: []
293
300
  });
294
301
  };
@@ -330,7 +337,7 @@ const containerStatuses = (pod) => {
330
337
  return /* @__PURE__ */ React__default.createElement(Fragment, {
331
338
  key: `${(_a2 = pod.metadata) == null ? void 0 : _a2.name}-${next.name}`
332
339
  }, /* @__PURE__ */ React__default.createElement(SubvalueCell, {
333
- value: /* @__PURE__ */ React__default.createElement(StatusError, null, "Container: ", next.name),
340
+ value: reason === "Completed" ? /* @__PURE__ */ React__default.createElement(StatusOK, null, "Container: ", next.name) : /* @__PURE__ */ React__default.createElement(StatusError, null, "Container: ", next.name),
334
341
  subvalue: reason
335
342
  }), /* @__PURE__ */ React__default.createElement("br", null));
336
343
  };
@@ -363,10 +370,36 @@ const renderCondition = (condition) => {
363
370
  }
364
371
  return [condition.type, /* @__PURE__ */ React__default.createElement(StatusAborted, null)];
365
372
  };
373
+ const currentToDeclaredResourceToPerc = (current, resource) => {
374
+ if (typeof current === "number" && typeof resource === "number") {
375
+ return `${Math.round(current / resource * 100)}%`;
376
+ }
377
+ const numerator = BigInt(current);
378
+ const denominator = BigInt(resource);
379
+ return `${numerator * BigInt(100) / denominator}%`;
380
+ };
381
+ const podStatusToCpuUtil = (podStatus) => {
382
+ const cpuUtil = podStatus.cpu;
383
+ let currentUsage = cpuUtil.currentUsage;
384
+ if (typeof cpuUtil.currentUsage === "number") {
385
+ currentUsage = cpuUtil.currentUsage / 10;
386
+ }
387
+ return /* @__PURE__ */ React__default.createElement(SubvalueCell, {
388
+ value: `requests: ${currentToDeclaredResourceToPerc(currentUsage, cpuUtil.requestTotal)}`,
389
+ subvalue: `limits: ${currentToDeclaredResourceToPerc(currentUsage, cpuUtil.limitTotal)}`
390
+ });
391
+ };
392
+ const podStatusToMemoryUtil = (podStatus) => {
393
+ const memUtil = podStatus.memory;
394
+ return /* @__PURE__ */ React__default.createElement(SubvalueCell, {
395
+ value: `requests: ${currentToDeclaredResourceToPerc(memUtil.currentUsage, memUtil.requestTotal)}`,
396
+ subvalue: `limits: ${currentToDeclaredResourceToPerc(memUtil.currentUsage, memUtil.limitTotal)}`
397
+ });
398
+ };
366
399
 
367
400
  const detectErrorsInObjects = (objects, kind, clusterName, errorMappers) => {
368
401
  var _a, _b;
369
- const errors = new Map();
402
+ const errors = /* @__PURE__ */ new Map();
370
403
  for (const object of objects) {
371
404
  for (const errorMapper of errorMappers) {
372
405
  if (errorMapper.errorExists(object)) {
@@ -514,7 +547,7 @@ const hpaErrorMappers = [
514
547
  const detectErrorsInHpa = (hpas, clusterName) => detectErrorsInObjects(hpas, "HorizontalPodAutoscaler", clusterName, hpaErrorMappers);
515
548
 
516
549
  const detectErrors = (objects) => {
517
- const errors = new Map();
550
+ const errors = /* @__PURE__ */ new Map();
518
551
  for (const clusterResponse of objects.items) {
519
552
  let clusterErrors = [];
520
553
  const groupedResponses = groupResponses(clusterResponse.resources);
@@ -572,7 +605,7 @@ const useKubernetesObjects = (entity, intervalMs = 1e4) => {
572
605
  };
573
606
  };
574
607
 
575
- const PodNamesWithErrorsContext = React__default.createContext(new Set());
608
+ const PodNamesWithErrorsContext = React__default.createContext(/* @__PURE__ */ new Set());
576
609
 
577
610
  const GroupedResponsesContext = React__default.createContext({
578
611
  pods: [],
@@ -582,6 +615,8 @@ const GroupedResponsesContext = React__default.createContext({
582
615
  configMaps: [],
583
616
  horizontalPodAutoscalers: [],
584
617
  ingresses: [],
618
+ jobs: [],
619
+ cronJobs: [],
585
620
  customResources: []
586
621
  });
587
622
 
@@ -750,7 +785,7 @@ const PodDrawerButton = withStyles({
750
785
  textTransform: "none"
751
786
  }
752
787
  })(Button);
753
- const ErrorPanel = ({cluster, errorMessage}) => /* @__PURE__ */ React__default.createElement(WarningPanel, {
788
+ const ErrorPanel = ({ cluster, errorMessage }) => /* @__PURE__ */ React__default.createElement(WarningPanel, {
754
789
  title: "There was a problem formatting the link to the Kubernetes dashboard",
755
790
  message: `Could not format the link to the dashboard of your cluster named '${cluster.name}'. Its dashboardApp property has been set to '${cluster.dashboardApp || "standard"}.'`
756
791
  }, errorMessage && /* @__PURE__ */ React__default.createElement(Typography, {
@@ -783,7 +818,7 @@ const KubernetesDrawerContent = ({
783
818
  const [isYaml, setIsYaml] = useState(false);
784
819
  const classes = useDrawerContentStyles();
785
820
  const cluster = useContext(ClusterContext);
786
- const {clusterLink, errorMessage} = tryFormatClusterLink({
821
+ const { clusterLink, errorMessage } = tryFormatClusterLink({
787
822
  dashboardUrl: cluster.dashboardUrl,
788
823
  dashboardApp: cluster.dashboardApp,
789
824
  object,
@@ -912,7 +947,11 @@ const PodDrawer = ({
912
947
  });
913
948
  };
914
949
 
915
- const columns = [
950
+ const PodNamesWithMetricsContext = React__default.createContext(/* @__PURE__ */ new Map());
951
+
952
+ const READY_COLUMNS = "READY";
953
+ const RESOURCE_COLUMNS = "RESOURCE";
954
+ const DEFAULT_COLUMNS = [
916
955
  {
917
956
  title: "name",
918
957
  highlight: true,
@@ -927,6 +966,12 @@ const columns = [
927
966
  return (_b = (_a = pod.status) == null ? void 0 : _a.phase) != null ? _b : "unknown";
928
967
  }
929
968
  },
969
+ {
970
+ title: "status",
971
+ render: containerStatuses
972
+ }
973
+ ];
974
+ const READY = [
930
975
  {
931
976
  title: "containers ready",
932
977
  align: "center",
@@ -937,13 +982,41 @@ const columns = [
937
982
  align: "center",
938
983
  render: totalRestarts,
939
984
  type: "numeric"
940
- },
941
- {
942
- title: "status",
943
- render: containerStatuses
944
985
  }
945
986
  ];
946
- const PodsTable = ({pods}) => {
987
+ const PodsTable = ({ pods, extraColumns = [] }) => {
988
+ const podNamesWithMetrics = useContext(PodNamesWithMetricsContext);
989
+ const columns = [...DEFAULT_COLUMNS];
990
+ if (extraColumns.includes(READY_COLUMNS)) {
991
+ columns.push(...READY);
992
+ }
993
+ if (extraColumns.includes(RESOURCE_COLUMNS)) {
994
+ const resourceColumns = [
995
+ {
996
+ title: "CPU usage %",
997
+ render: (pod) => {
998
+ var _a, _b;
999
+ const metrics = podNamesWithMetrics.get((_b = (_a = pod.metadata) == null ? void 0 : _a.name) != null ? _b : "");
1000
+ if (!metrics) {
1001
+ return "unknown";
1002
+ }
1003
+ return podStatusToCpuUtil(metrics);
1004
+ }
1005
+ },
1006
+ {
1007
+ title: "Memory usage %",
1008
+ render: (pod) => {
1009
+ var _a, _b;
1010
+ const metrics = podNamesWithMetrics.get((_b = (_a = pod.metadata) == null ? void 0 : _a.name) != null ? _b : "");
1011
+ if (!metrics) {
1012
+ return "unknown";
1013
+ }
1014
+ return podStatusToMemoryUtil(metrics);
1015
+ }
1016
+ }
1017
+ ];
1018
+ columns.push(...resourceColumns);
1019
+ }
947
1020
  const tableStyle = {
948
1021
  minWidth: "0",
949
1022
  width: "100%"
@@ -951,7 +1024,7 @@ const PodsTable = ({pods}) => {
951
1024
  return /* @__PURE__ */ React__default.createElement("div", {
952
1025
  style: tableStyle
953
1026
  }, /* @__PURE__ */ React__default.createElement(Table, {
954
- options: {paging: true, search: false},
1027
+ options: { paging: true, search: false },
955
1028
  data: pods,
956
1029
  columns
957
1030
  }));
@@ -1068,7 +1141,7 @@ const DeploymentSummary = ({
1068
1141
  item: true,
1069
1142
  xs: 1
1070
1143
  }, /* @__PURE__ */ React__default.createElement(Divider, {
1071
- style: {height: "5em"},
1144
+ style: { height: "5em" },
1072
1145
  orientation: "vertical"
1073
1146
  })), hpa && /* @__PURE__ */ React__default.createElement(Grid, {
1074
1147
  item: true,
@@ -1118,7 +1191,7 @@ const DeploymentAccordion = ({
1118
1191
  return podNamesWithErrors.has((_b = (_a = p.metadata) == null ? void 0 : _a.name) != null ? _b : "");
1119
1192
  });
1120
1193
  return /* @__PURE__ */ React__default.createElement(Accordion, {
1121
- TransitionProps: {unmountOnExit: true}
1194
+ TransitionProps: { unmountOnExit: true }
1122
1195
  }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1123
1196
  expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1124
1197
  }, /* @__PURE__ */ React__default.createElement(DeploymentSummary, {
@@ -1127,7 +1200,8 @@ const DeploymentAccordion = ({
1127
1200
  numberOfPodsWithErrors: podsWithErrors.length,
1128
1201
  hpa: matchingHpa
1129
1202
  })), /* @__PURE__ */ React__default.createElement(AccordionDetails, null, /* @__PURE__ */ React__default.createElement(PodsTable, {
1130
- pods: ownedPods
1203
+ pods: ownedPods,
1204
+ extraColumns: [READY_COLUMNS, RESOURCE_COLUMNS]
1131
1205
  })));
1132
1206
  };
1133
1207
  const DeploymentsAccordions = ({}) => {
@@ -1185,7 +1259,7 @@ const IngressDrawer = ({
1185
1259
  }, "Ingress"))));
1186
1260
  };
1187
1261
 
1188
- const IngressSummary = ({ingress}) => {
1262
+ const IngressSummary = ({ ingress }) => {
1189
1263
  return /* @__PURE__ */ React__default.createElement(Grid, {
1190
1264
  container: true,
1191
1265
  direction: "row",
@@ -1200,20 +1274,20 @@ const IngressSummary = ({ingress}) => {
1200
1274
  item: true,
1201
1275
  xs: 1
1202
1276
  }, /* @__PURE__ */ React__default.createElement(Divider, {
1203
- style: {height: "5em"},
1277
+ style: { height: "5em" },
1204
1278
  orientation: "vertical"
1205
1279
  })));
1206
1280
  };
1207
- const IngressCard = ({ingress}) => {
1281
+ const IngressCard = ({ ingress }) => {
1208
1282
  return /* @__PURE__ */ React__default.createElement(StructuredMetadataTable, {
1209
1283
  metadata: {
1210
1284
  ...ingress.spec
1211
1285
  }
1212
1286
  });
1213
1287
  };
1214
- const IngressAccordion = ({ingress}) => {
1288
+ const IngressAccordion = ({ ingress }) => {
1215
1289
  return /* @__PURE__ */ React__default.createElement(Accordion, {
1216
- TransitionProps: {unmountOnExit: true}
1290
+ TransitionProps: { unmountOnExit: true }
1217
1291
  }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1218
1292
  expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1219
1293
  }, /* @__PURE__ */ React__default.createElement(IngressSummary, {
@@ -1268,7 +1342,7 @@ const ServiceDrawer = ({
1268
1342
  }, "Service"))));
1269
1343
  };
1270
1344
 
1271
- const ServiceSummary = ({service}) => {
1345
+ const ServiceSummary = ({ service }) => {
1272
1346
  var _a, _b;
1273
1347
  return /* @__PURE__ */ React__default.createElement(Grid, {
1274
1348
  container: true,
@@ -1284,7 +1358,7 @@ const ServiceSummary = ({service}) => {
1284
1358
  item: true,
1285
1359
  xs: 1
1286
1360
  }, /* @__PURE__ */ React__default.createElement(Divider, {
1287
- style: {height: "5em"},
1361
+ style: { height: "5em" },
1288
1362
  orientation: "vertical"
1289
1363
  })), /* @__PURE__ */ React__default.createElement(Grid, {
1290
1364
  item: true
@@ -1292,7 +1366,7 @@ const ServiceSummary = ({service}) => {
1292
1366
  variant: "subtitle2"
1293
1367
  }, "Type: ", (_b = (_a = service.spec) == null ? void 0 : _a.type) != null ? _b : "?")));
1294
1368
  };
1295
- const ServiceCard = ({service}) => {
1369
+ const ServiceCard = ({ service }) => {
1296
1370
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1297
1371
  const metadata = {};
1298
1372
  if ((_d = (_c = (_b = (_a = service.status) == null ? void 0 : _a.loadBalancer) == null ? void 0 : _b.ingress) == null ? void 0 : _c.length) != null ? _d : -1 > 0) {
@@ -1312,9 +1386,9 @@ const ServiceCard = ({service}) => {
1312
1386
  }
1313
1387
  });
1314
1388
  };
1315
- const ServiceAccordion = ({service}) => {
1389
+ const ServiceAccordion = ({ service }) => {
1316
1390
  return /* @__PURE__ */ React__default.createElement(Accordion, {
1317
- TransitionProps: {unmountOnExit: true}
1391
+ TransitionProps: { unmountOnExit: true }
1318
1392
  }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1319
1393
  expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1320
1394
  }, /* @__PURE__ */ React__default.createElement(ServiceSummary, {
@@ -1339,6 +1413,214 @@ const ServicesAccordions = ({}) => {
1339
1413
  }))));
1340
1414
  };
1341
1415
 
1416
+ const JobDrawer = ({
1417
+ job,
1418
+ expanded
1419
+ }) => {
1420
+ var _a, _b;
1421
+ return /* @__PURE__ */ React__default.createElement(KubernetesDrawer, {
1422
+ object: job,
1423
+ expanded,
1424
+ kind: "Job",
1425
+ renderObject: (jobObj) => {
1426
+ var _a2, _b2, _c, _d, _e, _f, _g, _h;
1427
+ return {
1428
+ parallelism: (_b2 = (_a2 = jobObj.spec) == null ? void 0 : _a2.parallelism) != null ? _b2 : "???",
1429
+ completions: (_d = (_c = jobObj.spec) == null ? void 0 : _c.completions) != null ? _d : "???",
1430
+ backoffLimit: (_f = (_e = jobObj.spec) == null ? void 0 : _e.backoffLimit) != null ? _f : "???",
1431
+ startTime: (_h = (_g = jobObj.status) == null ? void 0 : _g.startTime) != null ? _h : "???"
1432
+ };
1433
+ }
1434
+ }, /* @__PURE__ */ React__default.createElement(Grid, {
1435
+ container: true,
1436
+ direction: "column",
1437
+ justifyContent: "flex-start",
1438
+ alignItems: "flex-start",
1439
+ spacing: 0
1440
+ }, /* @__PURE__ */ React__default.createElement(Grid, {
1441
+ item: true
1442
+ }, /* @__PURE__ */ React__default.createElement(Typography, {
1443
+ variant: "h6"
1444
+ }, (_b = (_a = job.metadata) == null ? void 0 : _a.name) != null ? _b : "unknown object")), /* @__PURE__ */ React__default.createElement(Grid, {
1445
+ item: true
1446
+ }, /* @__PURE__ */ React__default.createElement(Typography, {
1447
+ color: "textSecondary",
1448
+ variant: "body1"
1449
+ }, "Job"))));
1450
+ };
1451
+
1452
+ const JobSummary = ({ job }) => {
1453
+ var _a, _b, _c, _d, _e, _f;
1454
+ return /* @__PURE__ */ React__default.createElement(Grid, {
1455
+ container: true,
1456
+ direction: "row",
1457
+ justifyContent: "flex-start",
1458
+ alignItems: "center"
1459
+ }, /* @__PURE__ */ React__default.createElement(Grid, {
1460
+ xs: 3,
1461
+ item: true
1462
+ }, /* @__PURE__ */ React__default.createElement(JobDrawer, {
1463
+ job
1464
+ })), /* @__PURE__ */ React__default.createElement(Grid, {
1465
+ item: true,
1466
+ xs: 1
1467
+ }, /* @__PURE__ */ React__default.createElement(Divider, {
1468
+ style: { height: "5em" },
1469
+ orientation: "vertical"
1470
+ })), /* @__PURE__ */ React__default.createElement(Grid, {
1471
+ item: true,
1472
+ container: true,
1473
+ xs: 8,
1474
+ direction: "column",
1475
+ justifyContent: "flex-start",
1476
+ alignItems: "flex-start"
1477
+ }, /* @__PURE__ */ React__default.createElement(Grid, {
1478
+ item: true
1479
+ }, ((_a = job.status) == null ? void 0 : _a.succeeded) && /* @__PURE__ */ React__default.createElement(StatusOK, null, "Succeeded"), ((_b = job.status) == null ? void 0 : _b.active) && /* @__PURE__ */ React__default.createElement(StatusPending, null, "Running"), ((_c = job.status) == null ? void 0 : _c.failed) && /* @__PURE__ */ React__default.createElement(StatusError, null, "Failed")), /* @__PURE__ */ React__default.createElement(Grid, {
1480
+ item: true
1481
+ }, "Start time: ", (_e = (_d = job.status) == null ? void 0 : _d.startTime) == null ? void 0 : _e.toString()), ((_f = job.status) == null ? void 0 : _f.completionTime) && /* @__PURE__ */ React__default.createElement(Grid, {
1482
+ item: true
1483
+ }, "Completion time: ", job.status.completionTime.toString())));
1484
+ };
1485
+ const JobAccordion = ({ job, ownedPods }) => {
1486
+ return /* @__PURE__ */ React__default.createElement(Accordion, {
1487
+ TransitionProps: { unmountOnExit: true }
1488
+ }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1489
+ expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1490
+ }, /* @__PURE__ */ React__default.createElement(JobSummary, {
1491
+ job
1492
+ })), /* @__PURE__ */ React__default.createElement(AccordionDetails, null, /* @__PURE__ */ React__default.createElement(PodsTable, {
1493
+ pods: ownedPods
1494
+ })));
1495
+ };
1496
+ const JobsAccordions = ({ jobs }) => {
1497
+ const groupedResponses = useContext(GroupedResponsesContext);
1498
+ return /* @__PURE__ */ React__default.createElement(Grid, {
1499
+ container: true,
1500
+ direction: "column",
1501
+ justifyContent: "flex-start",
1502
+ alignItems: "flex-start"
1503
+ }, jobs.map((job, i) => /* @__PURE__ */ React__default.createElement(Grid, {
1504
+ container: true,
1505
+ item: true,
1506
+ key: i,
1507
+ xs: true
1508
+ }, /* @__PURE__ */ React__default.createElement(Grid, {
1509
+ item: true,
1510
+ xs: true
1511
+ }, /* @__PURE__ */ React__default.createElement(JobAccordion, {
1512
+ ownedPods: getOwnedResources(job, groupedResponses.pods),
1513
+ job
1514
+ })))));
1515
+ };
1516
+
1517
+ const CronJobDrawer = ({
1518
+ cronJob,
1519
+ expanded
1520
+ }) => {
1521
+ var _a, _b, _c;
1522
+ const namespace = (_a = cronJob.metadata) == null ? void 0 : _a.namespace;
1523
+ return /* @__PURE__ */ React__default.createElement(KubernetesDrawer, {
1524
+ object: cronJob,
1525
+ expanded,
1526
+ kind: "CronJob",
1527
+ renderObject: (cronJobObj) => {
1528
+ var _a2, _b2, _c2, _d, _e, _f, _g, _h;
1529
+ return {
1530
+ schedule: (_b2 = (_a2 = cronJobObj.spec) == null ? void 0 : _a2.schedule) != null ? _b2 : "???",
1531
+ startingDeadlineSeconds: (_d = (_c2 = cronJobObj.spec) == null ? void 0 : _c2.startingDeadlineSeconds) != null ? _d : "???",
1532
+ concurrencyPolicy: (_f = (_e = cronJobObj.spec) == null ? void 0 : _e.concurrencyPolicy) != null ? _f : "???",
1533
+ lastScheduleTime: (_h = (_g = cronJobObj.status) == null ? void 0 : _g.lastScheduleTime) != null ? _h : "???"
1534
+ };
1535
+ }
1536
+ }, /* @__PURE__ */ React__default.createElement(Grid, {
1537
+ container: true,
1538
+ direction: "column",
1539
+ justifyContent: "flex-start",
1540
+ alignItems: "flex-start",
1541
+ spacing: 0
1542
+ }, /* @__PURE__ */ React__default.createElement(Grid, {
1543
+ item: true
1544
+ }, /* @__PURE__ */ React__default.createElement(Typography, {
1545
+ variant: "h5"
1546
+ }, (_c = (_b = cronJob.metadata) == null ? void 0 : _b.name) != null ? _c : "unknown object")), /* @__PURE__ */ React__default.createElement(Grid, {
1547
+ item: true
1548
+ }, /* @__PURE__ */ React__default.createElement(Typography, {
1549
+ color: "textSecondary",
1550
+ variant: "body1"
1551
+ }, "CronJob")), namespace && /* @__PURE__ */ React__default.createElement(Grid, {
1552
+ item: true
1553
+ }, /* @__PURE__ */ React__default.createElement(Chip, {
1554
+ size: "small",
1555
+ label: `namespace: ${namespace}`
1556
+ }))));
1557
+ };
1558
+
1559
+ const CronJobSummary = ({ cronJob }) => {
1560
+ var _a, _b;
1561
+ return /* @__PURE__ */ React__default.createElement(Grid, {
1562
+ container: true,
1563
+ direction: "row",
1564
+ justifyContent: "flex-start",
1565
+ alignItems: "center"
1566
+ }, /* @__PURE__ */ React__default.createElement(Grid, {
1567
+ xs: 3,
1568
+ item: true
1569
+ }, /* @__PURE__ */ React__default.createElement(CronJobDrawer, {
1570
+ cronJob
1571
+ })), /* @__PURE__ */ React__default.createElement(Grid, {
1572
+ item: true,
1573
+ xs: 1
1574
+ }, /* @__PURE__ */ React__default.createElement(Divider, {
1575
+ style: { height: "5em" },
1576
+ orientation: "vertical"
1577
+ })), /* @__PURE__ */ React__default.createElement(Grid, {
1578
+ item: true,
1579
+ container: true,
1580
+ xs: 5,
1581
+ direction: "column",
1582
+ justifyContent: "flex-start",
1583
+ alignItems: "flex-start"
1584
+ }, /* @__PURE__ */ React__default.createElement(Grid, {
1585
+ item: true
1586
+ }, ((_a = cronJob.spec) == null ? void 0 : _a.suspend) ? /* @__PURE__ */ React__default.createElement(StatusError, null, "Suspended") : /* @__PURE__ */ React__default.createElement(StatusOK, null, "Active")), /* @__PURE__ */ React__default.createElement(Grid, {
1587
+ item: true
1588
+ }, /* @__PURE__ */ React__default.createElement(Typography, {
1589
+ variant: "body1"
1590
+ }, "Schedule:", " ", ((_b = cronJob.spec) == null ? void 0 : _b.schedule) ? `${cronJob.spec.schedule} (${cronstrue.toString(cronJob.spec.schedule)})` : "N/A"))));
1591
+ };
1592
+ const CronJobAccordion = ({ cronJob, ownedJobs }) => {
1593
+ return /* @__PURE__ */ React__default.createElement(Accordion, {
1594
+ TransitionProps: { unmountOnExit: true }
1595
+ }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1596
+ expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1597
+ }, /* @__PURE__ */ React__default.createElement(CronJobSummary, {
1598
+ cronJob
1599
+ })), /* @__PURE__ */ React__default.createElement(AccordionDetails, null, /* @__PURE__ */ React__default.createElement(JobsAccordions, {
1600
+ jobs: ownedJobs.reverse()
1601
+ })));
1602
+ };
1603
+ const CronJobsAccordions = ({}) => {
1604
+ const groupedResponses = useContext(GroupedResponsesContext);
1605
+ return /* @__PURE__ */ React__default.createElement(Grid, {
1606
+ container: true,
1607
+ direction: "column",
1608
+ justifyContent: "flex-start",
1609
+ alignItems: "flex-start"
1610
+ }, groupedResponses.cronJobs.map((cronJob, i) => /* @__PURE__ */ React__default.createElement(Grid, {
1611
+ container: true,
1612
+ item: true,
1613
+ key: i,
1614
+ xs: true
1615
+ }, /* @__PURE__ */ React__default.createElement(Grid, {
1616
+ item: true,
1617
+ xs: true
1618
+ }, /* @__PURE__ */ React__default.createElement(CronJobAccordion, {
1619
+ ownedJobs: getOwnedResources(cronJob, groupedResponses.jobs),
1620
+ cronJob
1621
+ })))));
1622
+ };
1623
+
1342
1624
  const RolloutDrawer = ({
1343
1625
  rollout,
1344
1626
  expanded
@@ -1438,7 +1720,7 @@ const RolloutSummary = ({
1438
1720
  item: true,
1439
1721
  xs: 1
1440
1722
  }, /* @__PURE__ */ React__default.createElement(Divider, {
1441
- style: {height: "5em"},
1723
+ style: { height: "5em" },
1442
1724
  orientation: "vertical"
1443
1725
  })), hpa && /* @__PURE__ */ React__default.createElement(Grid, {
1444
1726
  item: true,
@@ -1486,7 +1768,7 @@ const RolloutSummary = ({
1486
1768
  }
1487
1769
  }, /* @__PURE__ */ React__default.createElement(PauseIcon, null), /* @__PURE__ */ React__default.createElement(Typography, {
1488
1770
  variant: "subtitle1"
1489
- }, "Paused (", DateTime.fromISO(pauseTime).toRelative({locale: "en"}), ")"))), abortedMessage && /* @__PURE__ */ React__default.createElement(Grid, {
1771
+ }, "Paused (", DateTime.fromISO(pauseTime).toRelative({ locale: "en" }), ")"))), abortedMessage && /* @__PURE__ */ React__default.createElement(Grid, {
1490
1772
  item: true,
1491
1773
  xs: 3
1492
1774
  }, AbortedTitle));
@@ -1507,7 +1789,7 @@ const RolloutAccordion = ({
1507
1789
  const abortedMessage = findAbortedMessage(rollout);
1508
1790
  return /* @__PURE__ */ React__default.createElement(Accordion, {
1509
1791
  defaultExpanded,
1510
- TransitionProps: {unmountOnExit: true}
1792
+ TransitionProps: { unmountOnExit: true }
1511
1793
  }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1512
1794
  expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1513
1795
  }, /* @__PURE__ */ React__default.createElement(RolloutSummary, {
@@ -1516,11 +1798,11 @@ const RolloutAccordion = ({
1516
1798
  numberOfPodsWithErrors: podsWithErrors.length,
1517
1799
  hpa: matchingHpa
1518
1800
  })), /* @__PURE__ */ React__default.createElement(AccordionDetails, null, /* @__PURE__ */ React__default.createElement("div", {
1519
- style: {width: "100%"}
1801
+ style: { width: "100%" }
1520
1802
  }, /* @__PURE__ */ React__default.createElement("div", null, /* @__PURE__ */ React__default.createElement(Typography, {
1521
1803
  variant: "h6"
1522
1804
  }, "Rollout status")), /* @__PURE__ */ React__default.createElement("div", {
1523
- style: {margin: "1rem"}
1805
+ style: { margin: "1rem" }
1524
1806
  }, abortedMessage && /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, AbortedTitle, /* @__PURE__ */ React__default.createElement(Typography, {
1525
1807
  variant: "subtitle2"
1526
1808
  }, abortedMessage)), /* @__PURE__ */ React__default.createElement(StepsProgress, {
@@ -1528,7 +1810,8 @@ const RolloutAccordion = ({
1528
1810
  steps: (_f = (_e = (_d = (_c = rollout.spec) == null ? void 0 : _c.strategy) == null ? void 0 : _d.canary) == null ? void 0 : _e.steps) != null ? _f : [],
1529
1811
  currentStepIndex
1530
1812
  })), /* @__PURE__ */ React__default.createElement("div", null, /* @__PURE__ */ React__default.createElement(PodsTable, {
1531
- pods: ownedPods
1813
+ pods: ownedPods,
1814
+ extraColumns: [READY_COLUMNS, RESOURCE_COLUMNS]
1532
1815
  })))));
1533
1816
  };
1534
1817
  const RolloutAccordions = ({
@@ -1610,7 +1893,7 @@ const DefaultCustomResourceSummary = ({
1610
1893
  item: true,
1611
1894
  xs: 1
1612
1895
  }, /* @__PURE__ */ React__default.createElement(Divider, {
1613
- style: {height: "5em"},
1896
+ style: { height: "5em" },
1614
1897
  orientation: "vertical"
1615
1898
  })));
1616
1899
  };
@@ -1621,7 +1904,7 @@ const DefaultCustomResourceAccordion = ({
1621
1904
  }) => {
1622
1905
  return /* @__PURE__ */ React__default.createElement(Accordion, {
1623
1906
  defaultExpanded,
1624
- TransitionProps: {unmountOnExit: true}
1907
+ TransitionProps: { unmountOnExit: true }
1625
1908
  }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1626
1909
  expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1627
1910
  }, /* @__PURE__ */ React__default.createElement(DefaultCustomResourceSummary, {
@@ -1711,7 +1994,7 @@ const ClusterSummary = ({
1711
1994
  item: true,
1712
1995
  xs: 1
1713
1996
  }, /* @__PURE__ */ React__default.createElement(Divider, {
1714
- style: {height: "4em"},
1997
+ style: { height: "4em" },
1715
1998
  orientation: "vertical"
1716
1999
  })), /* @__PURE__ */ React__default.createElement(Grid, {
1717
2000
  item: true,
@@ -1726,16 +2009,26 @@ const ClusterSummary = ({
1726
2009
  item: true
1727
2010
  }, numberOfPodsWithErrors > 0 ? /* @__PURE__ */ React__default.createElement(StatusError, null, numberOfPodsWithErrors, " pods with errors") : /* @__PURE__ */ React__default.createElement(StatusOK, null, "No pods with errors"))));
1728
2011
  };
1729
- const Cluster = ({clusterObjects, podsWithErrors}) => {
2012
+ const Cluster = ({ clusterObjects, podsWithErrors }) => {
1730
2013
  const groupedResponses = groupResponses(clusterObjects.resources);
2014
+ const podNameToMetrics = clusterObjects.podMetrics.flat().reduce((accum, next) => {
2015
+ var _a;
2016
+ const name = (_a = next.pod.metadata) == null ? void 0 : _a.name;
2017
+ if (name !== void 0) {
2018
+ accum.set(name, next);
2019
+ }
2020
+ return accum;
2021
+ }, /* @__PURE__ */ new Map());
1731
2022
  return /* @__PURE__ */ React__default.createElement(ClusterContext.Provider, {
1732
2023
  value: clusterObjects.cluster
1733
2024
  }, /* @__PURE__ */ React__default.createElement(GroupedResponsesContext.Provider, {
1734
2025
  value: groupedResponses
2026
+ }, /* @__PURE__ */ React__default.createElement(PodNamesWithMetricsContext.Provider, {
2027
+ value: podNameToMetrics
1735
2028
  }, /* @__PURE__ */ React__default.createElement(PodNamesWithErrorsContext.Provider, {
1736
2029
  value: podsWithErrors
1737
2030
  }, /* @__PURE__ */ React__default.createElement(Accordion, {
1738
- TransitionProps: {unmountOnExit: true}
2031
+ TransitionProps: { unmountOnExit: true }
1739
2032
  }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1740
2033
  expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1741
2034
  }, /* @__PURE__ */ React__default.createElement(ClusterSummary, {
@@ -1753,14 +2046,16 @@ const Cluster = ({clusterObjects, podsWithErrors}) => {
1753
2046
  item: true
1754
2047
  }, /* @__PURE__ */ React__default.createElement(IngressesAccordions, null)), /* @__PURE__ */ React__default.createElement(Grid, {
1755
2048
  item: true
1756
- }, /* @__PURE__ */ React__default.createElement(ServicesAccordions, null))))))));
2049
+ }, /* @__PURE__ */ React__default.createElement(ServicesAccordions, null)), /* @__PURE__ */ React__default.createElement(Grid, {
2050
+ item: true
2051
+ }, /* @__PURE__ */ React__default.createElement(CronJobsAccordions, null)))))))));
1757
2052
  };
1758
2053
 
1759
- const KubernetesContent = ({entity}) => {
2054
+ const KubernetesContent = ({ entity }) => {
1760
2055
  var _a;
1761
- const {kubernetesObjects, error} = useKubernetesObjects(entity);
2056
+ const { kubernetesObjects, error } = useKubernetesObjects(entity);
1762
2057
  const clustersWithErrors = (_a = kubernetesObjects == null ? void 0 : kubernetesObjects.items.filter((r) => r.errors.length > 0)) != null ? _a : [];
1763
- const detectedErrors = kubernetesObjects !== void 0 ? detectErrors(kubernetesObjects) : new Map();
2058
+ const detectedErrors = kubernetesObjects !== void 0 ? detectErrors(kubernetesObjects) : /* @__PURE__ */ new Map();
1764
2059
  return /* @__PURE__ */ React__default.createElement(Page, {
1765
2060
  themeId: "tool"
1766
2061
  }, /* @__PURE__ */ React__default.createElement(Content, null, kubernetesObjects === void 0 && error === void 0 && /* @__PURE__ */ React__default.createElement(Progress, null), clustersWithErrors.length > 0 && /* @__PURE__ */ React__default.createElement(Grid, {
@@ -1838,7 +2133,7 @@ const isKubernetesAvailable = (entity) => {
1838
2133
  };
1839
2134
  const Router = (_props) => {
1840
2135
  var _a, _b;
1841
- const {entity} = useEntity();
2136
+ const { entity } = useEntity();
1842
2137
  const kubernetesAnnotationValue = (_a = entity.metadata.annotations) == null ? void 0 : _a[KUBERNETES_ANNOTATION];
1843
2138
  const kubernetesLabelSelectorQueryAnnotationValue = (_b = entity.metadata.annotations) == null ? void 0 : _b[KUBERNETES_LABEL_SELECTOR_QUERY_ANNOTATION];
1844
2139
  if (kubernetesAnnotationValue || kubernetesLabelSelectorQueryAnnotationValue) {
@@ -1864,5 +2159,5 @@ var Router$1 = /*#__PURE__*/Object.freeze({
1864
2159
  Router: Router
1865
2160
  });
1866
2161
 
1867
- export { EntityKubernetesContent, KubernetesAuthProviders, Router, clusterLinksFormatters, formatClusterLink, isKubernetesAvailable, kubernetesApiRef, kubernetesAuthProvidersApiRef, kubernetesPlugin, kubernetesPlugin as plugin };
2162
+ export { EntityKubernetesContent, KubernetesAuthProviders, KubernetesBackendClient, Router, clusterLinksFormatters, formatClusterLink, isKubernetesAvailable, kubernetesApiRef, kubernetesAuthProvidersApiRef, kubernetesPlugin, kubernetesPlugin as plugin };
1868
2163
  //# sourceMappingURL=index.esm.js.map