@backstage/plugin-kubernetes 0.4.22 → 0.5.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.
package/dist/index.esm.js CHANGED
@@ -4,14 +4,14 @@ 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, StatusPending, 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';
11
11
  import OpenInNewIcon from '@material-ui/icons/OpenInNew';
12
12
  import { withStyles } from '@material-ui/core/styles';
13
13
  import jsYaml from 'js-yaml';
14
- import { useInterval } from 'react-use';
14
+ import useInterval from 'react-use/lib/useInterval';
15
15
  import cronstrue from 'cronstrue';
16
16
  import lodash from 'lodash';
17
17
  import PauseIcon from '@material-ui/icons/Pause';
@@ -45,7 +45,7 @@ class KubernetesBackendClient {
45
45
  method: "POST",
46
46
  headers: {
47
47
  "Content-Type": "application/json",
48
- ...idToken && {Authorization: `Bearer ${idToken}`}
48
+ ...idToken && { Authorization: `Bearer ${idToken}` }
49
49
  },
50
50
  body: JSON.stringify(requestBody)
51
51
  });
@@ -60,7 +60,7 @@ class KubernetesBackendClient {
60
60
  const response = await fetch(url, {
61
61
  method: "GET",
62
62
  headers: {
63
- ...idToken && {Authorization: `Bearer ${idToken}`}
63
+ ...idToken && { Authorization: `Bearer ${idToken}` }
64
64
  }
65
65
  });
66
66
  return (await this.handleResponse(response)).items;
@@ -68,13 +68,11 @@ class KubernetesBackendClient {
68
68
  }
69
69
 
70
70
  const kubernetesApiRef = createApiRef({
71
- id: "plugin.kubernetes.service",
72
- description: "Used by the Kubernetes plugin to make requests to accompanying backend"
71
+ id: "plugin.kubernetes.service"
73
72
  });
74
73
 
75
74
  const kubernetesAuthProvidersApiRef = createApiRef({
76
- id: "plugin.kubernetes-auth-providers.service",
77
- description: "Used by the Kubernetes plugin to fetch KubernetesAuthProviders"
75
+ id: "plugin.kubernetes-auth-providers.service"
78
76
  });
79
77
 
80
78
  class GoogleKubernetesAuthProvider {
@@ -86,7 +84,7 @@ class GoogleKubernetesAuthProvider {
86
84
  if ("auth" in requestBody) {
87
85
  requestBody.auth.google = googleAuthToken;
88
86
  } else {
89
- requestBody.auth = {google: googleAuthToken};
87
+ requestBody.auth = { google: googleAuthToken };
90
88
  }
91
89
  return requestBody;
92
90
  }
@@ -106,7 +104,7 @@ class AwsKubernetesAuthProvider {
106
104
 
107
105
  class KubernetesAuthProviders {
108
106
  constructor(options) {
109
- this.kubernetesAuthProviderMap = new Map();
107
+ this.kubernetesAuthProviderMap = /* @__PURE__ */ new Map();
110
108
  this.kubernetesAuthProviderMap.set("google", new GoogleKubernetesAuthProvider(options.googleAuthApi));
111
109
  this.kubernetesAuthProviderMap.set("serviceAccount", new ServiceAccountKubernetesAuthProvider());
112
110
  this.kubernetesAuthProviderMap.set("aws", new AwsKubernetesAuthProvider());
@@ -132,13 +130,13 @@ const kubernetesPlugin = createPlugin({
132
130
  discoveryApi: discoveryApiRef,
133
131
  identityApi: identityApiRef
134
132
  },
135
- factory: ({discoveryApi, identityApi}) => new KubernetesBackendClient({discoveryApi, identityApi})
133
+ factory: ({ discoveryApi, identityApi }) => new KubernetesBackendClient({ discoveryApi, identityApi })
136
134
  }),
137
135
  createApiFactory({
138
136
  api: kubernetesAuthProvidersApiRef,
139
- deps: {googleAuthApi: googleAuthApiRef},
140
- factory: ({googleAuthApi}) => {
141
- return new KubernetesAuthProviders({googleAuthApi});
137
+ deps: { googleAuthApi: googleAuthApiRef },
138
+ factory: ({ googleAuthApi }) => {
139
+ return new KubernetesAuthProviders({ googleAuthApi });
142
140
  }
143
141
  })
144
142
  ],
@@ -241,7 +239,7 @@ const ErrorEmptyState = () => {
241
239
  "data-testid": "emptyStateImg"
242
240
  })));
243
241
  };
244
- const ErrorReporting = ({detectedErrors}) => {
242
+ const ErrorReporting = ({ detectedErrors }) => {
245
243
  const errors = Array.from(detectedErrors.values()).flat().sort(sortBySeverity);
246
244
  return /* @__PURE__ */ React.createElement(React.Fragment, null, errors.length === 0 ? /* @__PURE__ */ React.createElement(InfoCard, {
247
245
  title: "Error Reporting"
@@ -249,7 +247,7 @@ const ErrorReporting = ({detectedErrors}) => {
249
247
  title: "Error Reporting",
250
248
  data: errors,
251
249
  columns,
252
- options: {paging: true, search: false}
250
+ options: { paging: true, search: false }
253
251
  }));
254
252
  };
255
253
 
@@ -339,7 +337,7 @@ const containerStatuses = (pod) => {
339
337
  return /* @__PURE__ */ React__default.createElement(Fragment, {
340
338
  key: `${(_a2 = pod.metadata) == null ? void 0 : _a2.name}-${next.name}`
341
339
  }, /* @__PURE__ */ React__default.createElement(SubvalueCell, {
342
- 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),
343
341
  subvalue: reason
344
342
  }), /* @__PURE__ */ React__default.createElement("br", null));
345
343
  };
@@ -372,10 +370,36 @@ const renderCondition = (condition) => {
372
370
  }
373
371
  return [condition.type, /* @__PURE__ */ React__default.createElement(StatusAborted, null)];
374
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
+ };
375
399
 
376
400
  const detectErrorsInObjects = (objects, kind, clusterName, errorMappers) => {
377
401
  var _a, _b;
378
- const errors = new Map();
402
+ const errors = /* @__PURE__ */ new Map();
379
403
  for (const object of objects) {
380
404
  for (const errorMapper of errorMappers) {
381
405
  if (errorMapper.errorExists(object)) {
@@ -523,7 +547,7 @@ const hpaErrorMappers = [
523
547
  const detectErrorsInHpa = (hpas, clusterName) => detectErrorsInObjects(hpas, "HorizontalPodAutoscaler", clusterName, hpaErrorMappers);
524
548
 
525
549
  const detectErrors = (objects) => {
526
- const errors = new Map();
550
+ const errors = /* @__PURE__ */ new Map();
527
551
  for (const clusterResponse of objects.items) {
528
552
  let clusterErrors = [];
529
553
  const groupedResponses = groupResponses(clusterResponse.resources);
@@ -581,7 +605,7 @@ const useKubernetesObjects = (entity, intervalMs = 1e4) => {
581
605
  };
582
606
  };
583
607
 
584
- const PodNamesWithErrorsContext = React__default.createContext(new Set());
608
+ const PodNamesWithErrorsContext = React__default.createContext(/* @__PURE__ */ new Set());
585
609
 
586
610
  const GroupedResponsesContext = React__default.createContext({
587
611
  pods: [],
@@ -600,18 +624,22 @@ const ClusterContext = React__default.createContext({
600
624
  name: ""
601
625
  });
602
626
 
603
- const kindMappings$2 = {
627
+ const kindMappings$3 = {
604
628
  deployment: "deployment",
629
+ pod: "pod",
605
630
  ingress: "ingress",
606
631
  service: "service",
607
632
  horizontalpodautoscaler: "deployment"
608
633
  };
609
634
  function standardFormatter(options) {
610
635
  var _a, _b, _c, _d;
636
+ if (!options.dashboardUrl) {
637
+ throw new Error("standard dashboard requires a dashboardUrl option");
638
+ }
611
639
  const result = new URL(options.dashboardUrl.href);
612
640
  const name = encodeURIComponent((_b = (_a = options.object.metadata) == null ? void 0 : _a.name) != null ? _b : "");
613
641
  const namespace = encodeURIComponent((_d = (_c = options.object.metadata) == null ? void 0 : _c.namespace) != null ? _d : "");
614
- const validKind = kindMappings$2[options.kind.toLocaleLowerCase("en-US")];
642
+ const validKind = kindMappings$3[options.kind.toLocaleLowerCase("en-US")];
615
643
  if (!result.pathname.endsWith("/")) {
616
644
  result.pathname += "/";
617
645
  }
@@ -626,7 +654,7 @@ function standardFormatter(options) {
626
654
  return result;
627
655
  }
628
656
 
629
- const kindMappings$1 = {
657
+ const kindMappings$2 = {
630
658
  deployment: "apps.deployment",
631
659
  ingress: "networking.k8s.io.ingress",
632
660
  service: "service",
@@ -634,10 +662,13 @@ const kindMappings$1 = {
634
662
  };
635
663
  function rancherFormatter(options) {
636
664
  var _a, _b, _c, _d;
665
+ if (!options.dashboardUrl) {
666
+ throw new Error("Rancher dashboard requires a dashboardUrl option");
667
+ }
637
668
  const basePath = new URL(options.dashboardUrl.href);
638
669
  const name = encodeURIComponent((_b = (_a = options.object.metadata) == null ? void 0 : _a.name) != null ? _b : "");
639
670
  const namespace = encodeURIComponent((_d = (_c = options.object.metadata) == null ? void 0 : _c.namespace) != null ? _d : "");
640
- const validKind = kindMappings$1[options.kind.toLocaleLowerCase("en-US")];
671
+ const validKind = kindMappings$2[options.kind.toLocaleLowerCase("en-US")];
641
672
  if (!basePath.pathname.endsWith("/")) {
642
673
  basePath.pathname += "/";
643
674
  }
@@ -650,7 +681,7 @@ function rancherFormatter(options) {
650
681
  return new URL(path, basePath);
651
682
  }
652
683
 
653
- const kindMappings = {
684
+ const kindMappings$1 = {
654
685
  deployment: "deployments",
655
686
  ingress: "ingresses",
656
687
  service: "services",
@@ -659,10 +690,13 @@ const kindMappings = {
659
690
  };
660
691
  function openshiftFormatter(options) {
661
692
  var _a, _b, _c, _d;
693
+ if (!options.dashboardUrl) {
694
+ throw new Error("OpenShift dashboard requires a dashboardUrl option");
695
+ }
662
696
  const basePath = new URL(options.dashboardUrl.href);
663
697
  const name = encodeURIComponent((_b = (_a = options.object.metadata) == null ? void 0 : _a.name) != null ? _b : "");
664
698
  const namespace = encodeURIComponent((_d = (_c = options.object.metadata) == null ? void 0 : _c.namespace) != null ? _d : "");
665
- const validKind = kindMappings[options.kind.toLocaleLowerCase("en-US")];
699
+ const validKind = kindMappings$1[options.kind.toLocaleLowerCase("en-US")];
666
700
  if (!basePath.pathname.endsWith("/")) {
667
701
  basePath.pathname += "/";
668
702
  }
@@ -690,8 +724,45 @@ function eksFormatter(_options) {
690
724
  throw new Error("EKS formatter is not yet implemented. Please, contribute!");
691
725
  }
692
726
 
693
- function gkeFormatter(_options) {
694
- throw new Error("GKE formatter is not yet implemented. Please, contribute!");
727
+ const kindMappings = {
728
+ deployment: "deployment",
729
+ pod: "pod",
730
+ ingress: "ingress",
731
+ service: "service",
732
+ horizontalpodautoscaler: "deployment"
733
+ };
734
+ function gkeFormatter(options) {
735
+ var _a, _b, _c, _d;
736
+ if (!options.dashboardParameters) {
737
+ throw new Error("GKE dashboard requires a dashboardParameters option");
738
+ }
739
+ const args = options.dashboardParameters;
740
+ if (typeof args.projectId !== "string") {
741
+ throw new Error('GKE dashboard requires a "projectId" of type string in the dashboardParameters option');
742
+ }
743
+ if (typeof args.region !== "string") {
744
+ throw new Error('GKE dashboard requires a "region" of type string in the dashboardParameters option');
745
+ }
746
+ if (typeof args.clusterName !== "string") {
747
+ throw new Error('GKE dashboard requires a "clusterName" of type string in the dashboardParameters option');
748
+ }
749
+ const basePath = new URL("https://console.cloud.google.com/");
750
+ const region = encodeURIComponent(args.region);
751
+ const clusterName = encodeURIComponent(args.clusterName);
752
+ const name = encodeURIComponent((_b = (_a = options.object.metadata) == null ? void 0 : _a.name) != null ? _b : "");
753
+ const namespace = encodeURIComponent((_d = (_c = options.object.metadata) == null ? void 0 : _c.namespace) != null ? _d : "");
754
+ const validKind = kindMappings[options.kind.toLocaleLowerCase("en-US")];
755
+ let path = "";
756
+ if (namespace && name && validKind) {
757
+ const kindsWithDetails = ["ingress", "pod"];
758
+ const landingPage = kindsWithDetails.includes(validKind) ? "details" : "overview";
759
+ path = `kubernetes/${validKind}/${region}/${clusterName}/${namespace}/${name}/${landingPage}`;
760
+ } else {
761
+ path = `kubernetes/clusters/details/${region}/${clusterName}/details`;
762
+ }
763
+ const result = new URL(path, basePath);
764
+ result.searchParams.set("project", args.projectId);
765
+ return result;
695
766
  }
696
767
 
697
768
  const clusterLinksFormatters = {
@@ -705,10 +776,10 @@ const clusterLinksFormatters = {
705
776
  const defaultFormatterName = "standard";
706
777
 
707
778
  function formatClusterLink(options) {
708
- if (!options.dashboardUrl) {
779
+ if (!options.dashboardUrl && !options.dashboardParameters) {
709
780
  return void 0;
710
781
  }
711
- if (!options.object) {
782
+ if (options.dashboardUrl && !options.object) {
712
783
  return options.dashboardUrl;
713
784
  }
714
785
  const app = options.dashboardApp || defaultFormatterName;
@@ -717,7 +788,8 @@ function formatClusterLink(options) {
717
788
  throw new Error(`Could not find Kubernetes dashboard app named '${app}'`);
718
789
  }
719
790
  const url = formatter({
720
- dashboardUrl: new URL(options.dashboardUrl),
791
+ dashboardUrl: options.dashboardUrl ? new URL(options.dashboardUrl) : void 0,
792
+ dashboardParameters: options.dashboardParameters,
721
793
  object: options.object,
722
794
  kind: options.kind
723
795
  });
@@ -761,7 +833,7 @@ const PodDrawerButton = withStyles({
761
833
  textTransform: "none"
762
834
  }
763
835
  })(Button);
764
- const ErrorPanel = ({cluster, errorMessage}) => /* @__PURE__ */ React__default.createElement(WarningPanel, {
836
+ const ErrorPanel = ({ cluster, errorMessage }) => /* @__PURE__ */ React__default.createElement(WarningPanel, {
765
837
  title: "There was a problem formatting the link to the Kubernetes dashboard",
766
838
  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"}.'`
767
839
  }, errorMessage && /* @__PURE__ */ React__default.createElement(Typography, {
@@ -794,9 +866,10 @@ const KubernetesDrawerContent = ({
794
866
  const [isYaml, setIsYaml] = useState(false);
795
867
  const classes = useDrawerContentStyles();
796
868
  const cluster = useContext(ClusterContext);
797
- const {clusterLink, errorMessage} = tryFormatClusterLink({
869
+ const { clusterLink, errorMessage } = tryFormatClusterLink({
798
870
  dashboardUrl: cluster.dashboardUrl,
799
871
  dashboardApp: cluster.dashboardApp,
872
+ dashboardParameters: cluster.dashboardParameters,
800
873
  object,
801
874
  kind
802
875
  });
@@ -923,6 +996,10 @@ const PodDrawer = ({
923
996
  });
924
997
  };
925
998
 
999
+ const PodNamesWithMetricsContext = React__default.createContext(/* @__PURE__ */ new Map());
1000
+
1001
+ const READY_COLUMNS = "READY";
1002
+ const RESOURCE_COLUMNS = "RESOURCE";
926
1003
  const DEFAULT_COLUMNS = [
927
1004
  {
928
1005
  title: "name",
@@ -943,7 +1020,52 @@ const DEFAULT_COLUMNS = [
943
1020
  render: containerStatuses
944
1021
  }
945
1022
  ];
946
- const PodsTable = ({pods, extraColumns = []}) => {
1023
+ const READY = [
1024
+ {
1025
+ title: "containers ready",
1026
+ align: "center",
1027
+ render: containersReady
1028
+ },
1029
+ {
1030
+ title: "total restarts",
1031
+ align: "center",
1032
+ render: totalRestarts,
1033
+ type: "numeric"
1034
+ }
1035
+ ];
1036
+ const PodsTable = ({ pods, extraColumns = [] }) => {
1037
+ const podNamesWithMetrics = useContext(PodNamesWithMetricsContext);
1038
+ const columns = [...DEFAULT_COLUMNS];
1039
+ if (extraColumns.includes(READY_COLUMNS)) {
1040
+ columns.push(...READY);
1041
+ }
1042
+ if (extraColumns.includes(RESOURCE_COLUMNS)) {
1043
+ const resourceColumns = [
1044
+ {
1045
+ title: "CPU usage %",
1046
+ render: (pod) => {
1047
+ var _a, _b;
1048
+ const metrics = podNamesWithMetrics.get((_b = (_a = pod.metadata) == null ? void 0 : _a.name) != null ? _b : "");
1049
+ if (!metrics) {
1050
+ return "unknown";
1051
+ }
1052
+ return podStatusToCpuUtil(metrics);
1053
+ }
1054
+ },
1055
+ {
1056
+ title: "Memory usage %",
1057
+ render: (pod) => {
1058
+ var _a, _b;
1059
+ const metrics = podNamesWithMetrics.get((_b = (_a = pod.metadata) == null ? void 0 : _a.name) != null ? _b : "");
1060
+ if (!metrics) {
1061
+ return "unknown";
1062
+ }
1063
+ return podStatusToMemoryUtil(metrics);
1064
+ }
1065
+ }
1066
+ ];
1067
+ columns.push(...resourceColumns);
1068
+ }
947
1069
  const tableStyle = {
948
1070
  minWidth: "0",
949
1071
  width: "100%"
@@ -951,9 +1073,9 @@ const PodsTable = ({pods, extraColumns = []}) => {
951
1073
  return /* @__PURE__ */ React__default.createElement("div", {
952
1074
  style: tableStyle
953
1075
  }, /* @__PURE__ */ React__default.createElement(Table, {
954
- options: {paging: true, search: false},
1076
+ options: { paging: true, search: false },
955
1077
  data: pods,
956
- columns: DEFAULT_COLUMNS.concat(extraColumns)
1078
+ columns
957
1079
  }));
958
1080
  };
959
1081
 
@@ -1047,19 +1169,6 @@ const getMatchingHpa = (ownerName, ownerKind, hpas) => {
1047
1169
  });
1048
1170
  };
1049
1171
 
1050
- const deploymentPodColumns = [
1051
- {
1052
- title: "containers ready",
1053
- align: "center",
1054
- render: containersReady
1055
- },
1056
- {
1057
- title: "total restarts",
1058
- align: "center",
1059
- render: totalRestarts,
1060
- type: "numeric"
1061
- }
1062
- ];
1063
1172
  const DeploymentSummary = ({
1064
1173
  deployment,
1065
1174
  numberOfCurrentPods,
@@ -1081,7 +1190,7 @@ const DeploymentSummary = ({
1081
1190
  item: true,
1082
1191
  xs: 1
1083
1192
  }, /* @__PURE__ */ React__default.createElement(Divider, {
1084
- style: {height: "5em"},
1193
+ style: { height: "5em" },
1085
1194
  orientation: "vertical"
1086
1195
  })), hpa && /* @__PURE__ */ React__default.createElement(Grid, {
1087
1196
  item: true,
@@ -1131,7 +1240,7 @@ const DeploymentAccordion = ({
1131
1240
  return podNamesWithErrors.has((_b = (_a = p.metadata) == null ? void 0 : _a.name) != null ? _b : "");
1132
1241
  });
1133
1242
  return /* @__PURE__ */ React__default.createElement(Accordion, {
1134
- TransitionProps: {unmountOnExit: true}
1243
+ TransitionProps: { unmountOnExit: true }
1135
1244
  }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1136
1245
  expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1137
1246
  }, /* @__PURE__ */ React__default.createElement(DeploymentSummary, {
@@ -1141,7 +1250,7 @@ const DeploymentAccordion = ({
1141
1250
  hpa: matchingHpa
1142
1251
  })), /* @__PURE__ */ React__default.createElement(AccordionDetails, null, /* @__PURE__ */ React__default.createElement(PodsTable, {
1143
1252
  pods: ownedPods,
1144
- extraColumns: deploymentPodColumns
1253
+ extraColumns: [READY_COLUMNS, RESOURCE_COLUMNS]
1145
1254
  })));
1146
1255
  };
1147
1256
  const DeploymentsAccordions = ({}) => {
@@ -1199,7 +1308,7 @@ const IngressDrawer = ({
1199
1308
  }, "Ingress"))));
1200
1309
  };
1201
1310
 
1202
- const IngressSummary = ({ingress}) => {
1311
+ const IngressSummary = ({ ingress }) => {
1203
1312
  return /* @__PURE__ */ React__default.createElement(Grid, {
1204
1313
  container: true,
1205
1314
  direction: "row",
@@ -1214,20 +1323,20 @@ const IngressSummary = ({ingress}) => {
1214
1323
  item: true,
1215
1324
  xs: 1
1216
1325
  }, /* @__PURE__ */ React__default.createElement(Divider, {
1217
- style: {height: "5em"},
1326
+ style: { height: "5em" },
1218
1327
  orientation: "vertical"
1219
1328
  })));
1220
1329
  };
1221
- const IngressCard = ({ingress}) => {
1330
+ const IngressCard = ({ ingress }) => {
1222
1331
  return /* @__PURE__ */ React__default.createElement(StructuredMetadataTable, {
1223
1332
  metadata: {
1224
1333
  ...ingress.spec
1225
1334
  }
1226
1335
  });
1227
1336
  };
1228
- const IngressAccordion = ({ingress}) => {
1337
+ const IngressAccordion = ({ ingress }) => {
1229
1338
  return /* @__PURE__ */ React__default.createElement(Accordion, {
1230
- TransitionProps: {unmountOnExit: true}
1339
+ TransitionProps: { unmountOnExit: true }
1231
1340
  }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1232
1341
  expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1233
1342
  }, /* @__PURE__ */ React__default.createElement(IngressSummary, {
@@ -1282,7 +1391,7 @@ const ServiceDrawer = ({
1282
1391
  }, "Service"))));
1283
1392
  };
1284
1393
 
1285
- const ServiceSummary = ({service}) => {
1394
+ const ServiceSummary = ({ service }) => {
1286
1395
  var _a, _b;
1287
1396
  return /* @__PURE__ */ React__default.createElement(Grid, {
1288
1397
  container: true,
@@ -1298,7 +1407,7 @@ const ServiceSummary = ({service}) => {
1298
1407
  item: true,
1299
1408
  xs: 1
1300
1409
  }, /* @__PURE__ */ React__default.createElement(Divider, {
1301
- style: {height: "5em"},
1410
+ style: { height: "5em" },
1302
1411
  orientation: "vertical"
1303
1412
  })), /* @__PURE__ */ React__default.createElement(Grid, {
1304
1413
  item: true
@@ -1306,7 +1415,7 @@ const ServiceSummary = ({service}) => {
1306
1415
  variant: "subtitle2"
1307
1416
  }, "Type: ", (_b = (_a = service.spec) == null ? void 0 : _a.type) != null ? _b : "?")));
1308
1417
  };
1309
- const ServiceCard = ({service}) => {
1418
+ const ServiceCard = ({ service }) => {
1310
1419
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1311
1420
  const metadata = {};
1312
1421
  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) {
@@ -1326,9 +1435,9 @@ const ServiceCard = ({service}) => {
1326
1435
  }
1327
1436
  });
1328
1437
  };
1329
- const ServiceAccordion = ({service}) => {
1438
+ const ServiceAccordion = ({ service }) => {
1330
1439
  return /* @__PURE__ */ React__default.createElement(Accordion, {
1331
- TransitionProps: {unmountOnExit: true}
1440
+ TransitionProps: { unmountOnExit: true }
1332
1441
  }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1333
1442
  expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1334
1443
  }, /* @__PURE__ */ React__default.createElement(ServiceSummary, {
@@ -1389,7 +1498,7 @@ const JobDrawer = ({
1389
1498
  }, "Job"))));
1390
1499
  };
1391
1500
 
1392
- const JobSummary = ({job}) => {
1501
+ const JobSummary = ({ job }) => {
1393
1502
  var _a, _b, _c, _d, _e, _f;
1394
1503
  return /* @__PURE__ */ React__default.createElement(Grid, {
1395
1504
  container: true,
@@ -1405,7 +1514,7 @@ const JobSummary = ({job}) => {
1405
1514
  item: true,
1406
1515
  xs: 1
1407
1516
  }, /* @__PURE__ */ React__default.createElement(Divider, {
1408
- style: {height: "5em"},
1517
+ style: { height: "5em" },
1409
1518
  orientation: "vertical"
1410
1519
  })), /* @__PURE__ */ React__default.createElement(Grid, {
1411
1520
  item: true,
@@ -1422,9 +1531,9 @@ const JobSummary = ({job}) => {
1422
1531
  item: true
1423
1532
  }, "Completion time: ", job.status.completionTime.toString())));
1424
1533
  };
1425
- const JobAccordion = ({job, ownedPods}) => {
1534
+ const JobAccordion = ({ job, ownedPods }) => {
1426
1535
  return /* @__PURE__ */ React__default.createElement(Accordion, {
1427
- TransitionProps: {unmountOnExit: true}
1536
+ TransitionProps: { unmountOnExit: true }
1428
1537
  }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1429
1538
  expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1430
1539
  }, /* @__PURE__ */ React__default.createElement(JobSummary, {
@@ -1433,7 +1542,7 @@ const JobAccordion = ({job, ownedPods}) => {
1433
1542
  pods: ownedPods
1434
1543
  })));
1435
1544
  };
1436
- const JobsAccordions = ({jobs}) => {
1545
+ const JobsAccordions = ({ jobs }) => {
1437
1546
  const groupedResponses = useContext(GroupedResponsesContext);
1438
1547
  return /* @__PURE__ */ React__default.createElement(Grid, {
1439
1548
  container: true,
@@ -1496,7 +1605,7 @@ const CronJobDrawer = ({
1496
1605
  }))));
1497
1606
  };
1498
1607
 
1499
- const CronJobSummary = ({cronJob}) => {
1608
+ const CronJobSummary = ({ cronJob }) => {
1500
1609
  var _a, _b;
1501
1610
  return /* @__PURE__ */ React__default.createElement(Grid, {
1502
1611
  container: true,
@@ -1512,7 +1621,7 @@ const CronJobSummary = ({cronJob}) => {
1512
1621
  item: true,
1513
1622
  xs: 1
1514
1623
  }, /* @__PURE__ */ React__default.createElement(Divider, {
1515
- style: {height: "5em"},
1624
+ style: { height: "5em" },
1516
1625
  orientation: "vertical"
1517
1626
  })), /* @__PURE__ */ React__default.createElement(Grid, {
1518
1627
  item: true,
@@ -1529,9 +1638,9 @@ const CronJobSummary = ({cronJob}) => {
1529
1638
  variant: "body1"
1530
1639
  }, "Schedule:", " ", ((_b = cronJob.spec) == null ? void 0 : _b.schedule) ? `${cronJob.spec.schedule} (${cronstrue.toString(cronJob.spec.schedule)})` : "N/A"))));
1531
1640
  };
1532
- const CronJobAccordion = ({cronJob, ownedJobs}) => {
1641
+ const CronJobAccordion = ({ cronJob, ownedJobs }) => {
1533
1642
  return /* @__PURE__ */ React__default.createElement(Accordion, {
1534
- TransitionProps: {unmountOnExit: true}
1643
+ TransitionProps: { unmountOnExit: true }
1535
1644
  }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1536
1645
  expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1537
1646
  }, /* @__PURE__ */ React__default.createElement(CronJobSummary, {
@@ -1660,7 +1769,7 @@ const RolloutSummary = ({
1660
1769
  item: true,
1661
1770
  xs: 1
1662
1771
  }, /* @__PURE__ */ React__default.createElement(Divider, {
1663
- style: {height: "5em"},
1772
+ style: { height: "5em" },
1664
1773
  orientation: "vertical"
1665
1774
  })), hpa && /* @__PURE__ */ React__default.createElement(Grid, {
1666
1775
  item: true,
@@ -1708,7 +1817,7 @@ const RolloutSummary = ({
1708
1817
  }
1709
1818
  }, /* @__PURE__ */ React__default.createElement(PauseIcon, null), /* @__PURE__ */ React__default.createElement(Typography, {
1710
1819
  variant: "subtitle1"
1711
- }, "Paused (", DateTime.fromISO(pauseTime).toRelative({locale: "en"}), ")"))), abortedMessage && /* @__PURE__ */ React__default.createElement(Grid, {
1820
+ }, "Paused (", DateTime.fromISO(pauseTime).toRelative({ locale: "en" }), ")"))), abortedMessage && /* @__PURE__ */ React__default.createElement(Grid, {
1712
1821
  item: true,
1713
1822
  xs: 3
1714
1823
  }, AbortedTitle));
@@ -1729,7 +1838,7 @@ const RolloutAccordion = ({
1729
1838
  const abortedMessage = findAbortedMessage(rollout);
1730
1839
  return /* @__PURE__ */ React__default.createElement(Accordion, {
1731
1840
  defaultExpanded,
1732
- TransitionProps: {unmountOnExit: true}
1841
+ TransitionProps: { unmountOnExit: true }
1733
1842
  }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1734
1843
  expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1735
1844
  }, /* @__PURE__ */ React__default.createElement(RolloutSummary, {
@@ -1738,11 +1847,11 @@ const RolloutAccordion = ({
1738
1847
  numberOfPodsWithErrors: podsWithErrors.length,
1739
1848
  hpa: matchingHpa
1740
1849
  })), /* @__PURE__ */ React__default.createElement(AccordionDetails, null, /* @__PURE__ */ React__default.createElement("div", {
1741
- style: {width: "100%"}
1850
+ style: { width: "100%" }
1742
1851
  }, /* @__PURE__ */ React__default.createElement("div", null, /* @__PURE__ */ React__default.createElement(Typography, {
1743
1852
  variant: "h6"
1744
1853
  }, "Rollout status")), /* @__PURE__ */ React__default.createElement("div", {
1745
- style: {margin: "1rem"}
1854
+ style: { margin: "1rem" }
1746
1855
  }, abortedMessage && /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, AbortedTitle, /* @__PURE__ */ React__default.createElement(Typography, {
1747
1856
  variant: "subtitle2"
1748
1857
  }, abortedMessage)), /* @__PURE__ */ React__default.createElement(StepsProgress, {
@@ -1750,7 +1859,8 @@ const RolloutAccordion = ({
1750
1859
  steps: (_f = (_e = (_d = (_c = rollout.spec) == null ? void 0 : _c.strategy) == null ? void 0 : _d.canary) == null ? void 0 : _e.steps) != null ? _f : [],
1751
1860
  currentStepIndex
1752
1861
  })), /* @__PURE__ */ React__default.createElement("div", null, /* @__PURE__ */ React__default.createElement(PodsTable, {
1753
- pods: ownedPods
1862
+ pods: ownedPods,
1863
+ extraColumns: [READY_COLUMNS, RESOURCE_COLUMNS]
1754
1864
  })))));
1755
1865
  };
1756
1866
  const RolloutAccordions = ({
@@ -1832,7 +1942,7 @@ const DefaultCustomResourceSummary = ({
1832
1942
  item: true,
1833
1943
  xs: 1
1834
1944
  }, /* @__PURE__ */ React__default.createElement(Divider, {
1835
- style: {height: "5em"},
1945
+ style: { height: "5em" },
1836
1946
  orientation: "vertical"
1837
1947
  })));
1838
1948
  };
@@ -1843,7 +1953,7 @@ const DefaultCustomResourceAccordion = ({
1843
1953
  }) => {
1844
1954
  return /* @__PURE__ */ React__default.createElement(Accordion, {
1845
1955
  defaultExpanded,
1846
- TransitionProps: {unmountOnExit: true}
1956
+ TransitionProps: { unmountOnExit: true }
1847
1957
  }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1848
1958
  expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1849
1959
  }, /* @__PURE__ */ React__default.createElement(DefaultCustomResourceSummary, {
@@ -1933,7 +2043,7 @@ const ClusterSummary = ({
1933
2043
  item: true,
1934
2044
  xs: 1
1935
2045
  }, /* @__PURE__ */ React__default.createElement(Divider, {
1936
- style: {height: "4em"},
2046
+ style: { height: "4em" },
1937
2047
  orientation: "vertical"
1938
2048
  })), /* @__PURE__ */ React__default.createElement(Grid, {
1939
2049
  item: true,
@@ -1948,16 +2058,26 @@ const ClusterSummary = ({
1948
2058
  item: true
1949
2059
  }, numberOfPodsWithErrors > 0 ? /* @__PURE__ */ React__default.createElement(StatusError, null, numberOfPodsWithErrors, " pods with errors") : /* @__PURE__ */ React__default.createElement(StatusOK, null, "No pods with errors"))));
1950
2060
  };
1951
- const Cluster = ({clusterObjects, podsWithErrors}) => {
2061
+ const Cluster = ({ clusterObjects, podsWithErrors }) => {
1952
2062
  const groupedResponses = groupResponses(clusterObjects.resources);
2063
+ const podNameToMetrics = clusterObjects.podMetrics.flat().reduce((accum, next) => {
2064
+ var _a;
2065
+ const name = (_a = next.pod.metadata) == null ? void 0 : _a.name;
2066
+ if (name !== void 0) {
2067
+ accum.set(name, next);
2068
+ }
2069
+ return accum;
2070
+ }, /* @__PURE__ */ new Map());
1953
2071
  return /* @__PURE__ */ React__default.createElement(ClusterContext.Provider, {
1954
2072
  value: clusterObjects.cluster
1955
2073
  }, /* @__PURE__ */ React__default.createElement(GroupedResponsesContext.Provider, {
1956
2074
  value: groupedResponses
2075
+ }, /* @__PURE__ */ React__default.createElement(PodNamesWithMetricsContext.Provider, {
2076
+ value: podNameToMetrics
1957
2077
  }, /* @__PURE__ */ React__default.createElement(PodNamesWithErrorsContext.Provider, {
1958
2078
  value: podsWithErrors
1959
2079
  }, /* @__PURE__ */ React__default.createElement(Accordion, {
1960
- TransitionProps: {unmountOnExit: true}
2080
+ TransitionProps: { unmountOnExit: true }
1961
2081
  }, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
1962
2082
  expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
1963
2083
  }, /* @__PURE__ */ React__default.createElement(ClusterSummary, {
@@ -1977,14 +2097,14 @@ const Cluster = ({clusterObjects, podsWithErrors}) => {
1977
2097
  item: true
1978
2098
  }, /* @__PURE__ */ React__default.createElement(ServicesAccordions, null)), /* @__PURE__ */ React__default.createElement(Grid, {
1979
2099
  item: true
1980
- }, /* @__PURE__ */ React__default.createElement(CronJobsAccordions, null))))))));
2100
+ }, /* @__PURE__ */ React__default.createElement(CronJobsAccordions, null)))))))));
1981
2101
  };
1982
2102
 
1983
- const KubernetesContent = ({entity}) => {
2103
+ const KubernetesContent = ({ entity }) => {
1984
2104
  var _a;
1985
- const {kubernetesObjects, error} = useKubernetesObjects(entity);
2105
+ const { kubernetesObjects, error } = useKubernetesObjects(entity);
1986
2106
  const clustersWithErrors = (_a = kubernetesObjects == null ? void 0 : kubernetesObjects.items.filter((r) => r.errors.length > 0)) != null ? _a : [];
1987
- const detectedErrors = kubernetesObjects !== void 0 ? detectErrors(kubernetesObjects) : new Map();
2107
+ const detectedErrors = kubernetesObjects !== void 0 ? detectErrors(kubernetesObjects) : /* @__PURE__ */ new Map();
1988
2108
  return /* @__PURE__ */ React__default.createElement(Page, {
1989
2109
  themeId: "tool"
1990
2110
  }, /* @__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, {
@@ -2062,7 +2182,7 @@ const isKubernetesAvailable = (entity) => {
2062
2182
  };
2063
2183
  const Router = (_props) => {
2064
2184
  var _a, _b;
2065
- const {entity} = useEntity();
2185
+ const { entity } = useEntity();
2066
2186
  const kubernetesAnnotationValue = (_a = entity.metadata.annotations) == null ? void 0 : _a[KUBERNETES_ANNOTATION];
2067
2187
  const kubernetesLabelSelectorQueryAnnotationValue = (_b = entity.metadata.annotations) == null ? void 0 : _b[KUBERNETES_LABEL_SELECTOR_QUERY_ANNOTATION];
2068
2188
  if (kubernetesAnnotationValue || kubernetesLabelSelectorQueryAnnotationValue) {
@@ -2088,5 +2208,5 @@ var Router$1 = /*#__PURE__*/Object.freeze({
2088
2208
  Router: Router
2089
2209
  });
2090
2210
 
2091
- export { EntityKubernetesContent, KubernetesAuthProviders, Router, clusterLinksFormatters, formatClusterLink, isKubernetesAvailable, kubernetesApiRef, kubernetesAuthProvidersApiRef, kubernetesPlugin, kubernetesPlugin as plugin };
2211
+ export { EntityKubernetesContent, KubernetesAuthProviders, KubernetesBackendClient, Router, clusterLinksFormatters, formatClusterLink, isKubernetesAvailable, kubernetesApiRef, kubernetesAuthProvidersApiRef, kubernetesPlugin, kubernetesPlugin as plugin };
2092
2212
  //# sourceMappingURL=index.esm.js.map