@backstage/plugin-kubernetes 0.9.2-next.0 → 0.9.2-next.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
@@ -5,8 +5,8 @@ import * as React from 'react';
5
5
  import React__default, { useState, useCallback, useContext, Fragment } from 'react';
6
6
  import { useEntity } from '@backstage/plugin-catalog-react';
7
7
  import { Routes, Route } from 'react-router-dom';
8
- import { Typography, Paper, makeStyles, createStyles, Dialog, DialogTitle, IconButton, DialogContent, Button, Card, CardHeader, CardContent, Grid, CardActions, FormControlLabel, Switch, Drawer, withStyles as withStyles$1, List, ListItem, Divider, ListItemText, Chip, Accordion, AccordionSummary, AccordionDetails, Stepper, Step, StepLabel } from '@material-ui/core';
9
- import { WarningPanel, Table, DismissableBanner, LogViewer, StructuredMetadataTable, CodeSnippet, LinkButton, StatusError, StatusOK, StatusWarning, ItemCardGrid, SubvalueCell, StatusAborted, StatusPending, Page, Content, Progress, MissingAnnotationEmptyState } from '@backstage/core-components';
8
+ import { Typography, Paper, makeStyles, createStyles, Dialog, DialogTitle, IconButton, DialogContent, Button, Card, CardHeader, CardContent, Grid, CardActions, FormControlLabel, Switch, Drawer, withStyles as withStyles$1, List, ListItem, Container, Tooltip, ListItemText, ListItemAvatar, Avatar, Divider, Chip, Accordion, AccordionSummary, AccordionDetails, Stepper, Step, StepLabel } from '@material-ui/core';
9
+ import { WarningPanel, Table, DismissableBanner, EmptyState, LogViewer, StructuredMetadataTable, CodeSnippet, LinkButton, StatusError, StatusOK, StatusWarning, ItemCardGrid, SubvalueCell, StatusAborted, StatusPending, Page, Content, Progress, MissingAnnotationEmptyState } from '@backstage/core-components';
10
10
  import lodash from 'lodash';
11
11
  import { DateTime } from 'luxon';
12
12
  import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
@@ -15,14 +15,22 @@ import useAsync from 'react-use/lib/useAsync';
15
15
  import SubjectIcon from '@material-ui/icons/Subject';
16
16
  import CloseIcon from '@material-ui/icons/Close';
17
17
  import OpenInNewIcon from '@material-ui/icons/OpenInNew';
18
- import { withStyles } from '@material-ui/core/styles';
18
+ import { withStyles, makeStyles as makeStyles$1, createStyles as createStyles$1 } from '@material-ui/core/styles';
19
19
  import useInterval from 'react-use/lib/useInterval';
20
20
  import useAsyncRetry from 'react-use/lib/useAsyncRetry';
21
21
  import jsyaml from 'js-yaml';
22
+ import Dialog$1 from '@material-ui/core/Dialog';
23
+ import DialogActions from '@material-ui/core/DialogActions';
24
+ import DialogContent$1 from '@material-ui/core/DialogContent';
25
+ import DialogTitle$1 from '@material-ui/core/DialogTitle';
26
+ import IconButton$1 from '@material-ui/core/IconButton';
27
+ import Typography$1 from '@material-ui/core/Typography';
28
+ import HelpIcon from '@material-ui/icons/Help';
29
+ import InfoIcon from '@material-ui/icons/Info';
30
+ import WarningIcon from '@material-ui/icons/Warning';
22
31
  import cronstrue from 'cronstrue';
23
32
  import PauseIcon from '@material-ui/icons/Pause';
24
33
  import ErrorOutlineIcon from '@material-ui/icons/ErrorOutline';
25
- import Typography$1 from '@material-ui/core/Typography';
26
34
  import EmptyStateImage from './assets/emptystate.svg';
27
35
 
28
36
  class KubernetesBackendClient {
@@ -296,15 +304,47 @@ class KubernetesProxyClient {
296
304
  }
297
305
  return await response.text();
298
306
  }
307
+ async handleJson(response) {
308
+ if (!response.ok) {
309
+ const payload = await response.text();
310
+ let message = `Request failed with ${response.status} ${response.statusText}, ${payload}`;
311
+ switch (response.status) {
312
+ case 404:
313
+ message = `Proxy request failed with ${response.status} ${response.statusText}, ${payload}`;
314
+ break;
315
+ default:
316
+ message = `Request failed with ${response.status} ${response.statusText}, ${payload}`;
317
+ }
318
+ throw new Error(message);
319
+ }
320
+ return await response.json();
321
+ }
322
+ async getEventsByInvolvedObjectName({
323
+ clusterName,
324
+ involvedObjectName,
325
+ namespace
326
+ }) {
327
+ return await this.kubernetesApi.proxy({
328
+ clusterName,
329
+ path: `/api/v1/namespaces/${namespace}/events?fieldSelector=involvedObject.name=${involvedObjectName}`,
330
+ init: {
331
+ method: "GET"
332
+ }
333
+ }).then((response) => this.handleJson(response)).then((eventList) => eventList.items);
334
+ }
299
335
  async getPodLogs({
300
336
  podName,
301
337
  namespace,
302
338
  clusterName,
303
- containerName
339
+ containerName,
340
+ previous
304
341
  }) {
305
342
  const params = new URLSearchParams({
306
343
  container: containerName
307
344
  });
345
+ if (previous) {
346
+ params.append("previous", "");
347
+ }
308
348
  return await this.kubernetesApi.proxy({
309
349
  clusterName,
310
350
  path: `/api/v1/namespaces/${namespace}/pods/${podName}/log?${params.toString()}`,
@@ -556,6 +596,124 @@ const podToContainerSpecsAndStatuses = (pod) => {
556
596
  }
557
597
  return result;
558
598
  };
599
+ const readinessProbeProposedFixes = (pod) => {
600
+ var _a, _b, _c, _d;
601
+ const firstUnreadyContainerStatus = (_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) == null ? void 0 : _b.find(
602
+ (cs) => {
603
+ return cs.ready === false;
604
+ }
605
+ );
606
+ return {
607
+ errorType: "ReadinessProbeFailed",
608
+ rootCauseExplanation: `The container ${firstUnreadyContainerStatus == null ? void 0 : firstUnreadyContainerStatus.name} failed to start properly, but is not crashing`,
609
+ actions: [
610
+ "Ensure that the container starts correctly locally",
611
+ "Check the container's logs looking for error during startup"
612
+ ],
613
+ type: "events",
614
+ podName: (_d = (_c = pod.metadata) == null ? void 0 : _c.name) != null ? _d : ""
615
+ };
616
+ };
617
+ const restartingPodProposedFixes = (pod) => {
618
+ var _a, _b, _c;
619
+ const lastTerminatedCs = ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).find(
620
+ (cs) => {
621
+ var _a2;
622
+ return ((_a2 = cs.lastState) == null ? void 0 : _a2.terminated) !== void 0;
623
+ }
624
+ );
625
+ const lastTerminated = (_c = lastTerminatedCs == null ? void 0 : lastTerminatedCs.lastState) == null ? void 0 : _c.terminated;
626
+ if (!lastTerminated) {
627
+ return void 0;
628
+ }
629
+ switch (lastTerminated == null ? void 0 : lastTerminated.reason) {
630
+ case "Unknown":
631
+ return {
632
+ // TODO check this one, it's more likely a cluster issue
633
+ errorType: "Unknown",
634
+ rootCauseExplanation: `This container has exited with a non-zero exit code (${lastTerminated.exitCode})`,
635
+ actions: ["Check the crash logs for stacktraces"],
636
+ container: lastTerminatedCs.name,
637
+ type: "logs"
638
+ };
639
+ case "Error":
640
+ return {
641
+ errorType: "Error",
642
+ rootCauseExplanation: `This container has exited with a non-zero exit code (${lastTerminated.exitCode})`,
643
+ actions: ["Check the crash logs for stacktraces"],
644
+ container: lastTerminatedCs.name,
645
+ type: "logs"
646
+ };
647
+ case "OOMKilled":
648
+ return {
649
+ errorType: "OOMKilled",
650
+ rootCauseExplanation: `The container "${lastTerminatedCs.name}" has crashed because it has tried to use more memory that it has been allocated`,
651
+ actions: [
652
+ `Increase the amount of memory assigned to the container`,
653
+ "Ensure the application is memory bounded and is not trying to consume too much memory"
654
+ ],
655
+ docsLink: "https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/#exceed-a-container-s-memory-limit",
656
+ type: "docs"
657
+ };
658
+ default:
659
+ return void 0;
660
+ }
661
+ };
662
+ const waitingProposedFix = (pod) => {
663
+ var _a, _b, _c, _d, _e;
664
+ const waitingCs = ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).find(
665
+ (cs) => {
666
+ var _a2;
667
+ return ((_a2 = cs.state) == null ? void 0 : _a2.waiting) !== void 0;
668
+ }
669
+ );
670
+ const waiting = ((_d = (_c = pod.status) == null ? void 0 : _c.containerStatuses) != null ? _d : []).map((cs) => {
671
+ var _a2;
672
+ return (_a2 = cs.state) == null ? void 0 : _a2.waiting;
673
+ }).find((w) => (w == null ? void 0 : w.reason) !== void 0);
674
+ switch (waiting == null ? void 0 : waiting.reason) {
675
+ case "InvalidImageName":
676
+ return {
677
+ errorType: "InvalidImageName",
678
+ rootCauseExplanation: "The image in the pod is invalid",
679
+ actions: ["Ensure the image name is correct and valid image name"],
680
+ type: "docs",
681
+ docsLink: "https://docs.docker.com/engine/reference/commandline/tag/#extended-description"
682
+ };
683
+ case "ImagePullBackOff":
684
+ return {
685
+ errorType: "ImagePullBackOff",
686
+ rootCauseExplanation: "The image either could not be found or Kubernetes does not have permission to pull it",
687
+ actions: [
688
+ "Ensure the image name is correct",
689
+ "Ensure Kubernetes has permission to pull this image"
690
+ ],
691
+ type: "docs",
692
+ docsLink: "https://kubernetes.io/docs/concepts/containers/images/#imagepullbackoff"
693
+ };
694
+ case "CrashLoopBackOff":
695
+ return {
696
+ errorType: "CrashLoopBackOff",
697
+ rootCauseExplanation: `The container ${waitingCs == null ? void 0 : waitingCs.name} has crashed many times, it will be exponentially restarted until it stops crashing`,
698
+ actions: ["Check the crash logs for stacktraces"],
699
+ type: "logs",
700
+ container: (_e = waitingCs == null ? void 0 : waitingCs.name) != null ? _e : "unknown"
701
+ };
702
+ case "CreateContainerConfigError":
703
+ return {
704
+ errorType: "CreateContainerConfigError",
705
+ rootCauseExplanation: "There is missing or mismatching configuration required to start the container",
706
+ actions: [
707
+ "Ensure ConfigMaps references in the Deployment manifest are correct and the keys exist",
708
+ "Ensure Secrets references in the Deployment manifest are correct and the keys exist"
709
+ ],
710
+ type: "docs",
711
+ docsLink: "https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/"
712
+ };
713
+ default:
714
+ return void 0;
715
+ }
716
+ };
559
717
  const podErrorMappers = [
560
718
  {
561
719
  detectErrors: (pod) => {
@@ -565,8 +723,7 @@ const podErrorMappers = [
565
723
  type: "readiness-probe-taking-too-long",
566
724
  message: `The container ${cs.container.name} failed to start properly, but is not crashing`,
567
725
  severity: 4,
568
- proposedFix: [],
569
- // TODO next PR
726
+ proposedFix: readinessProbeProposedFixes(pod),
570
727
  sourceRef: {
571
728
  name: (_b = (_a = pod.metadata) == null ? void 0 : _a.name) != null ? _b : "unknown pod",
572
729
  namespace: (_d = (_c = pod.metadata) == null ? void 0 : _c.namespace) != null ? _d : "unknown namespace",
@@ -590,8 +747,7 @@ const podErrorMappers = [
590
747
  type: "container-waiting",
591
748
  message: (_c = (_b2 = (_a2 = cs.state) == null ? void 0 : _a2.waiting) == null ? void 0 : _b2.message) != null ? _c : "container waiting",
592
749
  severity: 4,
593
- proposedFix: [],
594
- // TODO next PR
750
+ proposedFix: waitingProposedFix(pod),
595
751
  sourceRef: {
596
752
  name: (_e = (_d = pod.metadata) == null ? void 0 : _d.name) != null ? _e : "unknown pod",
597
753
  namespace: (_g = (_f = pod.metadata) == null ? void 0 : _f.namespace) != null ? _g : "unknown namespace",
@@ -612,8 +768,7 @@ const podErrorMappers = [
612
768
  type: "containers-restarting",
613
769
  message: `container=${cs.name} restarted ${cs.restartCount} times`,
614
770
  severity: 4,
615
- proposedFix: [],
616
- // TODO next PR
771
+ proposedFix: restartingPodProposedFixes(pod),
617
772
  sourceRef: {
618
773
  name: (_b2 = (_a2 = pod.metadata) == null ? void 0 : _a2.name) != null ? _b2 : "unknown pod",
619
774
  namespace: (_d = (_c = pod.metadata) == null ? void 0 : _c.namespace) != null ? _d : "unknown namespace",
@@ -638,8 +793,6 @@ const deploymentErrorMappers = [
638
793
  type: "condition-message-present",
639
794
  message: (_a2 = c.message) != null ? _a2 : "",
640
795
  severity: 6,
641
- proposedFix: [],
642
- // TODO next PR
643
796
  sourceRef: {
644
797
  name: (_c = (_b2 = deployment.metadata) == null ? void 0 : _b2.name) != null ? _c : "unknown hpa",
645
798
  namespace: (_e = (_d = deployment.metadata) == null ? void 0 : _d.namespace) != null ? _e : "unknown namespace",
@@ -664,8 +817,6 @@ const hpaErrorMappers = [
664
817
  type: "hpa-max-current-replicas",
665
818
  message: `Current number of replicas (${(_d = hpa.status) == null ? void 0 : _d.currentReplicas}) is equal to the configured max number of replicas (${(_f = (_e = hpa.spec) == null ? void 0 : _e.maxReplicas) != null ? _f : -1})`,
666
819
  severity: 8,
667
- proposedFix: [],
668
- // TODO next PR
669
820
  sourceRef: {
670
821
  name: (_h = (_g = hpa.metadata) == null ? void 0 : _g.name) != null ? _h : "unknown hpa",
671
822
  namespace: (_j = (_i = hpa.metadata) == null ? void 0 : _i.namespace) != null ? _j : "unknown namespace",
@@ -703,21 +854,26 @@ const detectErrors = (objects) => {
703
854
  return errors;
704
855
  };
705
856
 
706
- const usePodLogs = ({ podScope }) => {
857
+ const usePodLogs = ({ containerScope, previous }) => {
707
858
  const kubernetesProxyApi = useApi(kubernetesProxyApiRef);
708
859
  return useAsync(async () => {
709
860
  return await kubernetesProxyApi.getPodLogs({
710
- podName: podScope.podName,
711
- namespace: podScope.podNamespace,
712
- containerName: podScope.containerName,
713
- clusterName: podScope.clusterName
861
+ podName: containerScope.podName,
862
+ namespace: containerScope.podNamespace,
863
+ containerName: containerScope.containerName,
864
+ clusterName: containerScope.clusterName,
865
+ previous
714
866
  });
715
- }, [JSON.stringify(podScope)]);
867
+ }, [JSON.stringify(containerScope)]);
716
868
  };
717
869
 
718
- const PodLogs = ({ podScope }) => {
870
+ const PodLogs = ({
871
+ containerScope,
872
+ previous
873
+ }) => {
719
874
  const { value, error, loading } = usePodLogs({
720
- podScope
875
+ containerScope,
876
+ previous
721
877
  });
722
878
  return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, error && /* @__PURE__ */ React__default.createElement(
723
879
  DismissableBanner,
@@ -733,14 +889,21 @@ const PodLogs = ({ podScope }) => {
733
889
  Paper,
734
890
  {
735
891
  elevation: 1,
736
- style: { height: "100%", width: "100%", minHeight: "30rem" }
892
+ style: { height: "100%", width: "100%", minHeight: "15rem" }
737
893
  },
738
894
  loading && /* @__PURE__ */ React__default.createElement(Skeleton, { variant: "rect", width: "100%", height: "100%" }),
739
- !loading && value !== void 0 && /* @__PURE__ */ React__default.createElement(LogViewer, { text: value.text })
895
+ !loading && value !== void 0 && (value.text === "" ? /* @__PURE__ */ React__default.createElement(
896
+ EmptyState,
897
+ {
898
+ missing: "data",
899
+ title: "No logs emitted",
900
+ description: "No logs were emitted by the container"
901
+ }
902
+ ) : /* @__PURE__ */ React__default.createElement(LogViewer, { text: value.text }))
740
903
  ));
741
904
  };
742
905
 
743
- const useStyles$1 = makeStyles(
906
+ const useStyles$2 = makeStyles(
744
907
  (theme) => createStyles({
745
908
  closeButton: {
746
909
  position: "absolute",
@@ -750,8 +913,8 @@ const useStyles$1 = makeStyles(
750
913
  }
751
914
  })
752
915
  );
753
- const PodLogsDialog = ({ podScope }) => {
754
- const classes = useStyles$1();
916
+ const PodLogsDialog = ({ containerScope }) => {
917
+ const classes = useStyles$2();
755
918
  const [open, setOpen] = useState(false);
756
919
  const openDialog = () => {
757
920
  setOpen(true);
@@ -759,7 +922,7 @@ const PodLogsDialog = ({ podScope }) => {
759
922
  const closeDialog = () => {
760
923
  setOpen(false);
761
924
  };
762
- return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(Dialog, { maxWidth: "xl", fullWidth: true, open, onClose: closeDialog }, /* @__PURE__ */ React__default.createElement(DialogTitle, { id: "dialog-title" }, podScope.podName, " - ", podScope.containerName, " logs on cluster", " ", podScope.clusterName, /* @__PURE__ */ React__default.createElement(
925
+ return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(Dialog, { maxWidth: "xl", fullWidth: true, open, onClose: closeDialog }, /* @__PURE__ */ React__default.createElement(DialogTitle, { id: "dialog-title" }, containerScope.podName, " - ", containerScope.containerName, " logs on cluster ", containerScope.clusterName, /* @__PURE__ */ React__default.createElement(
763
926
  IconButton,
764
927
  {
765
928
  "aria-label": "close",
@@ -767,7 +930,7 @@ const PodLogsDialog = ({ podScope }) => {
767
930
  onClick: closeDialog
768
931
  },
769
932
  /* @__PURE__ */ React__default.createElement(CloseIcon, null)
770
- )), /* @__PURE__ */ React__default.createElement(DialogContent, null, /* @__PURE__ */ React__default.createElement(PodLogs, { podScope }))), /* @__PURE__ */ React__default.createElement(
933
+ )), /* @__PURE__ */ React__default.createElement(DialogContent, null, /* @__PURE__ */ React__default.createElement(PodLogs, { containerScope }))), /* @__PURE__ */ React__default.createElement(
771
934
  Button,
772
935
  {
773
936
  variant: "outlined",
@@ -853,7 +1016,7 @@ const ContainerCard = ({
853
1016
  )))), /* @__PURE__ */ React__default.createElement(CardActions, { disableSpacing: true }, /* @__PURE__ */ React__default.createElement(
854
1017
  PodLogsDialog,
855
1018
  {
856
- podScope: {
1019
+ containerScope: {
857
1020
  containerName: containerStatus.name,
858
1021
  ...podScope
859
1022
  }
@@ -1507,6 +1670,159 @@ const PendingPodContent = ({ pod }) => {
1507
1670
  return /* @__PURE__ */ React__default.createElement(Grid, { container: true, spacing: 2 }, /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "h5" }, "Pod is Pending. Conditions:"), /* @__PURE__ */ React__default.createElement(List, null, startupConditions.map((c) => /* @__PURE__ */ React__default.createElement(ListItem, { key: c.type }, /* @__PURE__ */ React__default.createElement(PodCondition, { condition: c }))))));
1508
1671
  };
1509
1672
 
1673
+ const useEvents = ({
1674
+ involvedObjectName,
1675
+ namespace,
1676
+ clusterName
1677
+ }) => {
1678
+ const kubernetesProxyApi = useApi(kubernetesProxyApiRef);
1679
+ return useAsync(async () => {
1680
+ return await kubernetesProxyApi.getEventsByInvolvedObjectName({
1681
+ involvedObjectName,
1682
+ namespace,
1683
+ clusterName
1684
+ });
1685
+ }, [involvedObjectName, namespace, clusterName]);
1686
+ };
1687
+
1688
+ const getAvatarByType = (type) => {
1689
+ return /* @__PURE__ */ React__default.createElement(ListItemAvatar, null, /* @__PURE__ */ React__default.createElement(Avatar, null, type === "Warning" ? /* @__PURE__ */ React__default.createElement(WarningIcon, null) : /* @__PURE__ */ React__default.createElement(InfoIcon, null)));
1690
+ };
1691
+ const EventsContent = ({
1692
+ events,
1693
+ warningEventsOnly
1694
+ }) => {
1695
+ if (events.length === 0) {
1696
+ return /* @__PURE__ */ React__default.createElement(Typography, null, "No events found");
1697
+ }
1698
+ return /* @__PURE__ */ React__default.createElement(Container, null, /* @__PURE__ */ React__default.createElement(Grid, null, /* @__PURE__ */ React__default.createElement(List, null, events.filter((event) => {
1699
+ if (warningEventsOnly) {
1700
+ return event.type === "Warning";
1701
+ }
1702
+ return true;
1703
+ }).map((event) => {
1704
+ var _a;
1705
+ const timeAgo = event.metadata.creationTimestamp ? DateTime.fromISO(event.metadata.creationTimestamp).toRelative(
1706
+ {
1707
+ locale: "en"
1708
+ }
1709
+ ) : "unknown";
1710
+ return /* @__PURE__ */ React__default.createElement(ListItem, { key: event.metadata.name }, /* @__PURE__ */ React__default.createElement(Tooltip, { title: `${(_a = event.type) != null ? _a : ""} event` }, getAvatarByType(event.type)), /* @__PURE__ */ React__default.createElement(
1711
+ ListItemText,
1712
+ {
1713
+ primary: `First event ${timeAgo} (count: ${event.count})`,
1714
+ secondary: `${event.reason}: ${event.message}`
1715
+ }
1716
+ ));
1717
+ }))));
1718
+ };
1719
+ const Events = ({
1720
+ involvedObjectName,
1721
+ namespace,
1722
+ clusterName,
1723
+ warningEventsOnly
1724
+ }) => {
1725
+ const { value, error, loading } = useEvents({
1726
+ involvedObjectName,
1727
+ namespace,
1728
+ clusterName
1729
+ });
1730
+ return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, error && /* @__PURE__ */ React__default.createElement(
1731
+ DismissableBanner,
1732
+ {
1733
+ ...{
1734
+ message: error.message,
1735
+ variant: "error",
1736
+ fixed: false
1737
+ },
1738
+ id: "events"
1739
+ }
1740
+ ), loading && /* @__PURE__ */ React__default.createElement(Skeleton, { variant: "rect", width: "100%", height: "100%" }), !loading && value !== void 0 && /* @__PURE__ */ React__default.createElement(EventsContent, { warningEventsOnly, events: value }));
1741
+ };
1742
+
1743
+ const useStyles$1 = makeStyles$1(
1744
+ (theme) => createStyles$1({
1745
+ closeButton: {
1746
+ position: "absolute",
1747
+ right: theme.spacing(1),
1748
+ top: theme.spacing(1),
1749
+ color: theme.palette.grey[500]
1750
+ }
1751
+ })
1752
+ );
1753
+ const FixDialog = ({
1754
+ open,
1755
+ pod,
1756
+ error,
1757
+ clusterName
1758
+ }) => {
1759
+ var _a;
1760
+ const [isOpen, setOpen] = useState(!!open);
1761
+ const classes = useStyles$1();
1762
+ const openDialog = () => {
1763
+ setOpen(true);
1764
+ };
1765
+ const closeDialog = () => {
1766
+ setOpen(false);
1767
+ };
1768
+ const pf = error.proposedFix;
1769
+ const dialogContent = () => {
1770
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
1771
+ return /* @__PURE__ */ React__default.createElement(Grid, { container: true }, /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(Typography$1, { variant: "h6" }, "Detected error:"), /* @__PURE__ */ React__default.createElement(Typography$1, null, error.message)), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(Typography$1, { variant: "h6" }, "Cause explanation:"), /* @__PURE__ */ React__default.createElement(Typography$1, null, (_b = (_a2 = error.proposedFix) == null ? void 0 : _a2.rootCauseExplanation) != null ? _b : "unknown")), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(Typography$1, { variant: "h6" }, "Fix:"), /* @__PURE__ */ React__default.createElement(Typography$1, null, /* @__PURE__ */ React__default.createElement("ul", null, ((_d = (_c = error.proposedFix) == null ? void 0 : _c.actions) != null ? _d : []).map((fix, i) => {
1772
+ var _a3, _b2;
1773
+ return /* @__PURE__ */ React__default.createElement("li", { key: `${(_b2 = (_a3 = pod.metadata) == null ? void 0 : _a3.name) != null ? _b2 : "unknown"}-pf-${i}` }, fix);
1774
+ })))), pf && pf.type === "logs" && /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(Typography$1, { variant: "h6" }, "Crash logs:")), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 9 }, /* @__PURE__ */ React__default.createElement(
1775
+ PodLogs,
1776
+ {
1777
+ previous: true,
1778
+ containerScope: {
1779
+ podName: (_f = (_e = pod.metadata) == null ? void 0 : _e.name) != null ? _f : "unknown",
1780
+ podNamespace: (_h = (_g = pod.metadata) == null ? void 0 : _g.namespace) != null ? _h : "unknown",
1781
+ clusterName,
1782
+ containerName: pf.container
1783
+ }
1784
+ }
1785
+ ))), pf && pf.type === "events" && /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(Typography$1, { variant: "h6" }, "Events:")), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 9 }, /* @__PURE__ */ React__default.createElement(
1786
+ Events,
1787
+ {
1788
+ warningEventsOnly: true,
1789
+ involvedObjectName: (_j = (_i = pod.metadata) == null ? void 0 : _i.name) != null ? _j : "",
1790
+ namespace: (_l = (_k = pod.metadata) == null ? void 0 : _k.namespace) != null ? _l : "",
1791
+ clusterName
1792
+ }
1793
+ ))));
1794
+ };
1795
+ return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(
1796
+ Button,
1797
+ {
1798
+ variant: "outlined",
1799
+ "aria-label": "fix issue",
1800
+ component: "label",
1801
+ onClick: openDialog,
1802
+ startIcon: /* @__PURE__ */ React__default.createElement(HelpIcon, null)
1803
+ },
1804
+ "Help"
1805
+ ), /* @__PURE__ */ React__default.createElement(Dialog$1, { maxWidth: "xl", fullWidth: true, open: isOpen, onClose: closeDialog }, /* @__PURE__ */ React__default.createElement(DialogTitle$1, { id: "dialog-title" }, (_a = pod.metadata) == null ? void 0 : _a.name, " - ", error.type, /* @__PURE__ */ React__default.createElement(
1806
+ IconButton$1,
1807
+ {
1808
+ "aria-label": "close",
1809
+ className: classes.closeButton,
1810
+ onClick: closeDialog
1811
+ },
1812
+ /* @__PURE__ */ React__default.createElement(CloseIcon, null)
1813
+ )), /* @__PURE__ */ React__default.createElement(DialogContent$1, null, dialogContent()), /* @__PURE__ */ React__default.createElement(DialogActions, null, pf && pf.type === "docs" && /* @__PURE__ */ React__default.createElement(
1814
+ LinkButton,
1815
+ {
1816
+ to: pf.docsLink,
1817
+ variant: "outlined",
1818
+ startIcon: /* @__PURE__ */ React__default.createElement(OpenInNewIcon, null),
1819
+ target: "_blank",
1820
+ rel: "noopener"
1821
+ },
1822
+ "Open docs"
1823
+ ))));
1824
+ };
1825
+
1510
1826
  const useStyles = makeStyles(
1511
1827
  (_theme) => createStyles({
1512
1828
  root: {
@@ -1528,13 +1844,20 @@ const ErrorList = ({ podAndErrors }) => {
1528
1844
  key: `${(_b = (_a = onlyPodWithErrors.pod.metadata) == null ? void 0 : _a.name) != null ? _b : "unknown"}-eli-${i}`
1529
1845
  },
1530
1846
  i > 0 && /* @__PURE__ */ React__default.createElement(Divider, { key: `error-divider${i}` }),
1531
- /* @__PURE__ */ React__default.createElement(ListItem, null, /* @__PURE__ */ React__default.createElement(
1847
+ /* @__PURE__ */ React__default.createElement(ListItem, null, /* @__PURE__ */ React__default.createElement(Grid, { container: true }, /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 9 }, /* @__PURE__ */ React__default.createElement(
1532
1848
  ListItemText,
1533
1849
  {
1534
1850
  primary: error.message,
1535
1851
  secondary: (_c = onlyPodWithErrors.pod.metadata) == null ? void 0 : _c.name
1536
1852
  }
1537
- ))
1853
+ )), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 3 }, /* @__PURE__ */ React__default.createElement(
1854
+ FixDialog,
1855
+ {
1856
+ pod: onlyPodWithErrors.pod,
1857
+ error,
1858
+ clusterName: onlyPodWithErrors.clusterName
1859
+ }
1860
+ ))))
1538
1861
  );
1539
1862
  });
1540
1863
  })));
@@ -3050,5 +3373,5 @@ var Router$1 = /*#__PURE__*/Object.freeze({
3050
3373
  Router: Router
3051
3374
  });
3052
3375
 
3053
- export { Cluster, ClusterContext, CronJobsAccordions, CustomResources, EntityKubernetesContent, ErrorPanel, ErrorReporting, GoogleKubernetesAuthProvider, GroupedResponsesContext, HorizontalPodAutoscalerDrawer, IngressesAccordions, JobsAccordions, KubernetesAuthProviders, KubernetesBackendClient, KubernetesContent, KubernetesDrawer, KubernetesDrawerContent, KubernetesProxyClient, KubernetesStructuredMetadataTableDrawer, LinkErrorPanel, PodDrawer, PodNamesWithErrorsContext, PodNamesWithMetricsContext, PodsTable, Router, ServerSideKubernetesAuthProvider, ServicesAccordions, clusterLinksFormatters, detectErrors, formatClusterLink, isKubernetesAvailable, kubernetesApiRef, kubernetesAuthProvidersApiRef, kubernetesPlugin, kubernetesProxyApiRef, kubernetesPlugin as plugin, useCustomResources, useKubernetesObjects };
3376
+ export { Cluster, ClusterContext, ContainerCard, CronJobsAccordions, CustomResources, EntityKubernetesContent, ErrorList, ErrorPanel, ErrorReporting, Events, EventsContent, FixDialog, GoogleKubernetesAuthProvider, GroupedResponsesContext, HorizontalPodAutoscalerDrawer, IngressesAccordions, JobsAccordions, KubernetesAuthProviders, KubernetesBackendClient, KubernetesContent, KubernetesDrawer, KubernetesDrawerContent, KubernetesProxyClient, KubernetesStructuredMetadataTableDrawer, LinkErrorPanel, PendingPodContent, PodDrawer, PodLogs, PodLogsDialog, PodNamesWithErrorsContext, PodNamesWithMetricsContext, PodsTable, Router, ServerSideKubernetesAuthProvider, ServicesAccordions, clusterLinksFormatters, detectErrors, formatClusterLink, isKubernetesAvailable, kubernetesApiRef, kubernetesAuthProvidersApiRef, kubernetesPlugin, kubernetesProxyApiRef, kubernetesPlugin as plugin, useCustomResources, useEvents, useKubernetesObjects, usePodLogs };
3054
3377
  //# sourceMappingURL=index.esm.js.map