@backstage/plugin-kubernetes-react 0.2.1 → 0.3.0-next.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # @backstage/plugin-kubernetes-react
2
2
 
3
+ ## 0.3.0-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 3c184af: Extracted common dialog component.
8
+ - Updated dependencies
9
+ - @backstage/core-components@0.14.0-next.0
10
+ - @backstage/catalog-model@1.4.4-next.0
11
+ - @backstage/core-plugin-api@1.8.3-next.0
12
+ - @backstage/errors@1.2.3
13
+ - @backstage/types@1.1.1
14
+ - @backstage/plugin-kubernetes-common@0.7.4-next.1
15
+
16
+ ## 0.3.0-next.0
17
+
18
+ ### Minor Changes
19
+
20
+ - 0d526c8: **BREAKING** The pod exec terminal is now disabled by default since there are several scenarios where it is known not to work. It can be re-enabled at your own risk by setting the config parameter `kubernetes.podExecTerminal.enabled` to `true`.
21
+
22
+ ### Patch Changes
23
+
24
+ - 536f67d: Fix broken XtermJS CSS import
25
+ - db1054b: Fixed a bug where the logs dialog and any other functionality depending on the proxy endpoint would fail for clusters configured with the OIDC auth provider.
26
+ - Updated dependencies
27
+ - @backstage/plugin-kubernetes-common@0.7.4-next.0
28
+ - @backstage/core-components@0.13.10
29
+ - @backstage/catalog-model@1.4.3
30
+ - @backstage/core-plugin-api@1.8.2
31
+ - @backstage/errors@1.2.3
32
+ - @backstage/types@1.1.1
33
+
3
34
  ## 0.2.1
4
35
 
5
36
  ### Patch Changes
package/config.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ import { PodExecTerminal } from './src/components/PodExecTerminal/PodExecTerminal';
2
+ /*
3
+ * Copyright 2023 The Backstage Authors
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ export interface Config {
19
+ kubernetes?: {
20
+ /**
21
+ * Pod Exec Terminal config
22
+ */
23
+ podExecTerminal?: {
24
+ /**
25
+ * Enable `PodExecTerminal` UI feature
26
+ * @visibility frontend
27
+ */
28
+ enabled?: boolean;
29
+ };
30
+ };
31
+ }
package/dist/index.d.ts CHANGED
@@ -860,6 +860,6 @@ declare const PodExecTerminal: (props: PodExecTerminalProps) => React__default.J
860
860
  *
861
861
  * @public
862
862
  */
863
- declare const PodExecTerminalDialog: (props: PodExecTerminalProps) => React__default.JSX.Element;
863
+ declare const PodExecTerminalDialog: (props: PodExecTerminalProps) => false | React__default.JSX.Element | undefined;
864
864
 
865
865
  export { AksClusterLinksFormatter, AksKubernetesAuthProvider, Cluster, ClusterContext, ClusterLinksFormatter, ClusterLinksFormatterOptions, ClusterProps, ContainerCard, ContainerCardProps, ContainerScope, CronJobsAccordions, CronJobsAccordionsProps, CustomResources, CustomResourcesProps, DEFAULT_FORMATTER_NAME, DetectedErrorsContext, EksClusterLinksFormatter, ErrorList, ErrorListProps, ErrorMatcher, ErrorPanel, ErrorPanelProps, ErrorReporting, ErrorReportingProps, Events, EventsContent, EventsContentProps, EventsOptions, EventsProps, FixDialog, FixDialogProps, FormatClusterLinkOptions, GkeClusterLinksFormatter, GoogleKubernetesAuthProvider, GroupedResponsesContext, HorizontalPodAutoscalerDrawer, IngressesAccordions, IngressesAccordionsProps, JobsAccordions, JobsAccordionsProps, KubernetesApi, KubernetesAuthProvider, KubernetesAuthProviders, KubernetesAuthProvidersApi, KubernetesBackendClient, KubernetesClusterLinkFormatter, KubernetesClusterLinkFormatterApi, KubernetesDrawer, KubernetesDrawerProps, KubernetesDrawerable, KubernetesObject, KubernetesObjects, KubernetesProxyApi, KubernetesProxyClient, KubernetesStructuredMetadataTableDrawer, KubernetesStructuredMetadataTableDrawerProps, LinkErrorPanel, LinkErrorPanelProps, ManifestYaml, ManifestYamlProps, OidcKubernetesAuthProvider, OpenshiftClusterLinksFormatter, PendingPodContent, PendingPodContentProps, PodAndErrors, PodColumns, PodDrawer, PodDrawerProps, PodExecTerminal, PodExecTerminalDialog, PodExecTerminalProps, PodLogs, PodLogsDialog, PodLogsDialogProps, PodLogsOptions, PodLogsProps, PodMetricsContext, PodMetricsMatcher, PodNamesWithErrorsContext, PodNamesWithMetricsContext, PodScope, PodsTable, PodsTablesProps, READY_COLUMNS, RESOURCE_COLUMNS, RancherClusterLinksFormatter, ResourceUtilization, ResourceUtilizationProps, ServerSideKubernetesAuthProvider, ServicesAccordions, ServicesAccordionsProps, StandardClusterLinksFormatter, getDefaultFormatters, kubernetesApiRef, kubernetesAuthProvidersApiRef, kubernetesClusterLinkFormatterApiRef, kubernetesProxyApiRef, useCustomResources, useEvents, useKubernetesObjects, useMatchingErrors, usePodLogs, usePodMetrics };
package/dist/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createApiRef, useApi, discoveryApiRef } from '@backstage/core-plugin-api';
1
+ import { useApi, configApiRef, createApiRef, discoveryApiRef } from '@backstage/core-plugin-api';
2
2
  import useAsync from 'react-use/lib/useAsync';
3
3
  import * as React from 'react';
4
4
  import React__default, { useCallback, useContext, useState, useEffect, useMemo, Fragment } from 'react';
@@ -11,8 +11,9 @@ import { groupResponses } from '@backstage/plugin-kubernetes-common';
11
11
  import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
12
12
  import { LinearGauge, DismissableBanner, EmptyState, LogViewer, StructuredMetadataTable, CodeSnippet, WarningPanel, LinkButton, StatusError, StatusOK, StatusWarning, ItemCardGrid, SubvalueCell, StatusAborted, Table, StatusPending } from '@backstage/core-components';
13
13
  import { DateTime } from 'luxon';
14
- import CloseIcon from '@material-ui/icons/Close';
15
14
  import OpenInBrowserIcon from '@material-ui/icons/OpenInBrowser';
15
+ import CloseIcon from '@material-ui/icons/Close';
16
+ import 'xterm/css/xterm.css';
16
17
  import { Terminal } from 'xterm';
17
18
  import { FitAddon } from 'xterm-addon-fit';
18
19
  import { AttachAddon } from 'xterm-addon-attach';
@@ -35,6 +36,11 @@ import lodash from 'lodash';
35
36
  import PauseIcon from '@material-ui/icons/Pause';
36
37
  import ErrorOutlineIcon from '@material-ui/icons/ErrorOutline';
37
38
 
39
+ const useIsPodExecTerminalEnabled = () => {
40
+ const configApi = useApi(configApiRef);
41
+ return configApi.getOptionalBoolean("kubernetes.podExecTerminal.enabled");
42
+ };
43
+
38
44
  const kubernetesApiRef = createApiRef({
39
45
  id: "plugin.kubernetes.service"
40
46
  });
@@ -422,8 +428,10 @@ class KubernetesBackendClient {
422
428
  }
423
429
  return cluster;
424
430
  }
425
- async getCredentials(authProvider) {
426
- return await this.kubernetesAuthProvidersApi.getCredentials(authProvider);
431
+ async getCredentials(authProvider, oidcTokenProvider) {
432
+ return await this.kubernetesAuthProvidersApi.getCredentials(
433
+ authProvider === "oidc" ? `${authProvider}.${oidcTokenProvider}` : authProvider
434
+ );
427
435
  }
428
436
  async getObjectsByEntity(requestBody) {
429
437
  return await this.postRequired(
@@ -459,7 +467,10 @@ class KubernetesBackendClient {
459
467
  const { authProvider, oidcTokenProvider } = await this.getCluster(
460
468
  options.clusterName
461
469
  );
462
- const kubernetesCredentials = await this.getCredentials(authProvider);
470
+ const kubernetesCredentials = await this.getCredentials(
471
+ authProvider,
472
+ oidcTokenProvider
473
+ );
463
474
  const url = `${await this.discoveryApi.getBaseUrl("kubernetes")}/proxy${options.path}`;
464
475
  const identityResponse = await this.identityApi.getCredentials();
465
476
  const headers = KubernetesBackendClient.getKubernetesHeaders(
@@ -842,6 +853,67 @@ const formatMillicores = (value) => {
842
853
  return `${(parseFloat(value.toString()) * 1e3).toFixed(0)}m`;
843
854
  };
844
855
 
856
+ const useStyles$3 = makeStyles(
857
+ (theme) => createStyles({
858
+ dialogPaper: { minHeight: "calc(100% - 64px)" },
859
+ dialogContent: { flexBasis: 0 },
860
+ closeButton: {
861
+ position: "absolute",
862
+ right: theme.spacing(1),
863
+ top: theme.spacing(1),
864
+ color: theme.palette.grey[500]
865
+ }
866
+ })
867
+ );
868
+ const KubernetesDialog = ({
869
+ buttonAriaLabel,
870
+ buttonIcon,
871
+ buttonText,
872
+ children,
873
+ disabled,
874
+ title
875
+ }) => {
876
+ const classes = useStyles$3();
877
+ const [open, setOpen] = useState(false);
878
+ const openDialog = () => {
879
+ setOpen(true);
880
+ };
881
+ const closeDialog = () => {
882
+ setOpen(false);
883
+ };
884
+ return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(
885
+ Dialog,
886
+ {
887
+ maxWidth: "xl",
888
+ fullWidth: true,
889
+ open,
890
+ onClose: closeDialog,
891
+ PaperProps: { className: classes.dialogPaper }
892
+ },
893
+ /* @__PURE__ */ React__default.createElement(DialogTitle, { id: "dialog-title" }, title, /* @__PURE__ */ React__default.createElement(
894
+ IconButton,
895
+ {
896
+ "aria-label": "close",
897
+ className: classes.closeButton,
898
+ onClick: closeDialog
899
+ },
900
+ /* @__PURE__ */ React__default.createElement(CloseIcon, null)
901
+ )),
902
+ /* @__PURE__ */ React__default.createElement(DialogContent, { className: classes.dialogContent }, children)
903
+ ), /* @__PURE__ */ React__default.createElement(
904
+ Button,
905
+ {
906
+ variant: "outlined",
907
+ "aria-label": buttonAriaLabel,
908
+ component: "label",
909
+ disabled,
910
+ onClick: openDialog,
911
+ startIcon: buttonIcon
912
+ },
913
+ buttonText
914
+ ));
915
+ };
916
+
845
917
  var __accessCheck = (obj, member, msg) => {
846
918
  if (!member.has(obj))
847
919
  throw TypeError("Cannot " + msg);
@@ -879,7 +951,17 @@ class PodExecTerminalAttachAddon extends AttachAddon {
879
951
  _textEncoder = new WeakMap();
880
952
 
881
953
  const hasSocketProtocol = (url) => /wss?:\/\//.test(url.toString());
954
+ const useStyles$2 = makeStyles(
955
+ (theme) => createStyles({
956
+ podExecTerminal: {
957
+ width: "100%",
958
+ height: "100%",
959
+ "& .xterm-screen": { padding: theme.spacing(1) }
960
+ }
961
+ })
962
+ );
882
963
  const PodExecTerminal = (props) => {
964
+ const classes = useStyles$2();
883
965
  const { containerName, podNamespace, podName } = props;
884
966
  const [baseUrl, setBaseUrl] = useState(window.location.host);
885
967
  const terminalRef = React__default.useRef(null);
@@ -941,68 +1023,25 @@ const PodExecTerminal = (props) => {
941
1023
  {
942
1024
  "data-testid": "terminal",
943
1025
  ref: terminalRef,
944
- style: {
945
- width: "100%",
946
- height: "100%"
947
- }
1026
+ className: classes.podExecTerminal
948
1027
  }
949
1028
  );
950
1029
  };
951
1030
 
952
- const useStyles$3 = makeStyles(
953
- (theme) => createStyles({
954
- dialogPaper: { minHeight: "calc(100% - 64px)" },
955
- dialogContent: { flexBasis: 0 },
956
- closeButton: {
957
- position: "absolute",
958
- right: theme.spacing(1),
959
- top: theme.spacing(1),
960
- color: theme.palette.grey[500]
961
- }
962
- })
963
- );
964
1031
  const PodExecTerminalDialog = (props) => {
965
- const classes = useStyles$3();
966
1032
  const { clusterName, containerName, podName } = props;
967
- const [open, setOpen] = useState(false);
968
1033
  const isPodExecTerminalSupported = useIsPodExecTerminalSupported();
969
- const openDialog = () => {
970
- setOpen(true);
971
- };
972
- const closeDialog = () => {
973
- setOpen(false);
974
- };
975
- return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, !isPodExecTerminalSupported.loading && isPodExecTerminalSupported.value && /* @__PURE__ */ React__default.createElement(
976
- Dialog,
1034
+ return !isPodExecTerminalSupported.loading && isPodExecTerminalSupported.value && /* @__PURE__ */ React__default.createElement(
1035
+ KubernetesDialog,
977
1036
  {
978
- maxWidth: false,
979
- fullWidth: true,
980
- open,
981
- onClose: closeDialog,
982
- PaperProps: { className: classes.dialogPaper }
983
- },
984
- /* @__PURE__ */ React__default.createElement(DialogTitle, { id: "dialog-title" }, podName, " - ", containerName, " terminal shell on cluster", " ", clusterName, /* @__PURE__ */ React__default.createElement(
985
- IconButton,
986
- {
987
- "aria-label": "close",
988
- className: classes.closeButton,
989
- onClick: closeDialog
990
- },
991
- /* @__PURE__ */ React__default.createElement(CloseIcon, null)
992
- )),
993
- /* @__PURE__ */ React__default.createElement(DialogContent, { className: classes.dialogContent }, /* @__PURE__ */ React__default.createElement(PodExecTerminal, { ...props }))
994
- ), /* @__PURE__ */ React__default.createElement(
995
- Button,
996
- {
997
- variant: "outlined",
998
- "aria-label": "open terminal",
999
- component: "label",
1037
+ buttonAriaLabel: "open terminal",
1038
+ buttonIcon: /* @__PURE__ */ React__default.createElement(OpenInBrowserIcon, null),
1039
+ buttonText: "Terminal",
1000
1040
  disabled: isPodExecTerminalSupported.loading || !isPodExecTerminalSupported.value,
1001
- onClick: openDialog,
1002
- startIcon: /* @__PURE__ */ React__default.createElement(OpenInBrowserIcon, null)
1041
+ title: `${podName} - ${containerName} terminal shell on cluster ${clusterName}`
1003
1042
  },
1004
- "Terminal"
1005
- ));
1043
+ /* @__PURE__ */ React__default.createElement(PodExecTerminal, { ...props })
1044
+ );
1006
1045
  };
1007
1046
 
1008
1047
  const getProgressColor = ({
@@ -1096,44 +1135,18 @@ const PodLogs = ({
1096
1135
  ));
1097
1136
  };
1098
1137
 
1099
- const useStyles$2 = makeStyles(
1100
- (theme) => createStyles({
1101
- closeButton: {
1102
- position: "absolute",
1103
- right: theme.spacing(1),
1104
- top: theme.spacing(1),
1105
- color: theme.palette.grey[500]
1106
- }
1107
- })
1108
- );
1109
1138
  const PodLogsDialog = ({ containerScope }) => {
1110
- const classes = useStyles$2();
1111
- const [open, setOpen] = useState(false);
1112
- const openDialog = () => {
1113
- setOpen(true);
1114
- };
1115
- const closeDialog = () => {
1116
- setOpen(false);
1117
- };
1118
- 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(
1119
- IconButton,
1120
- {
1121
- "aria-label": "close",
1122
- className: classes.closeButton,
1123
- onClick: closeDialog
1124
- },
1125
- /* @__PURE__ */ React__default.createElement(CloseIcon, null)
1126
- )), /* @__PURE__ */ React__default.createElement(DialogContent, null, /* @__PURE__ */ React__default.createElement(PodLogs, { containerScope }))), /* @__PURE__ */ React__default.createElement(
1127
- Button,
1139
+ return /* @__PURE__ */ React__default.createElement(
1140
+ KubernetesDialog,
1128
1141
  {
1129
- variant: "outlined",
1130
- "aria-label": "get logs",
1131
- component: "label",
1132
- onClick: openDialog,
1133
- startIcon: /* @__PURE__ */ React__default.createElement(SubjectIcon, null)
1142
+ buttonAriaLabel: "get logs",
1143
+ buttonIcon: /* @__PURE__ */ React__default.createElement(SubjectIcon, null),
1144
+ buttonText: "Logs",
1145
+ disabled: false,
1146
+ title: `${containerScope.podName} - ${containerScope.containerName} logs on cluster ${containerScope.clusterName}`
1134
1147
  },
1135
- "Logs"
1136
- ));
1148
+ /* @__PURE__ */ React__default.createElement(PodLogs, { containerScope })
1149
+ );
1137
1150
  };
1138
1151
 
1139
1152
  const getContainerHealthChecks = (containerSpec, containerStatus) => {
@@ -1172,6 +1185,7 @@ const ContainerCard = ({
1172
1185
  containerMetrics
1173
1186
  }) => {
1174
1187
  var _a, _b;
1188
+ const isPodExecTerminalEnabled = useIsPodExecTerminalEnabled();
1175
1189
  if (containerSpec === void 0) {
1176
1190
  return /* @__PURE__ */ React__default.createElement(Typography, null, "error reading pod from cluster");
1177
1191
  }
@@ -1259,7 +1273,7 @@ const ContainerCard = ({
1259
1273
  ...podScope
1260
1274
  }
1261
1275
  }
1262
- ), /* @__PURE__ */ React__default.createElement(
1276
+ ), isPodExecTerminalEnabled && /* @__PURE__ */ React__default.createElement(
1263
1277
  PodExecTerminalDialog,
1264
1278
  {
1265
1279
  clusterName: podScope.clusterName,
@@ -3302,5 +3316,5 @@ const ErrorReporting = ({ detectedErrors }) => {
3302
3316
  ));
3303
3317
  };
3304
3318
 
3305
- export { AksClusterLinksFormatter, AksKubernetesAuthProvider, Cluster, ClusterContext, ContainerCard, CronJobsAccordions, CustomResources, DEFAULT_FORMATTER_NAME, DetectedErrorsContext, EksClusterLinksFormatter, ErrorList, ErrorPanel, ErrorReporting, Events, EventsContent, FixDialog, GkeClusterLinksFormatter, GoogleKubernetesAuthProvider, GroupedResponsesContext, HorizontalPodAutoscalerDrawer, IngressesAccordions, JobsAccordions, KubernetesAuthProviders, KubernetesBackendClient, KubernetesClusterLinkFormatter, KubernetesDrawer, KubernetesProxyClient, KubernetesStructuredMetadataTableDrawer, LinkErrorPanel, ManifestYaml, OidcKubernetesAuthProvider, OpenshiftClusterLinksFormatter, PendingPodContent, PodDrawer, PodExecTerminal, PodExecTerminalDialog, PodLogs, PodLogsDialog, PodMetricsContext, PodNamesWithErrorsContext, PodNamesWithMetricsContext, PodsTable, READY_COLUMNS, RESOURCE_COLUMNS, RancherClusterLinksFormatter, ResourceUtilization, ServerSideKubernetesAuthProvider, ServicesAccordions, StandardClusterLinksFormatter, getDefaultFormatters, kubernetesApiRef, kubernetesAuthProvidersApiRef, kubernetesClusterLinkFormatterApiRef, kubernetesProxyApiRef, useCustomResources, useEvents, useIsPodExecTerminalSupported, useKubernetesObjects, useMatchingErrors, usePodLogs, usePodMetrics };
3319
+ export { AksClusterLinksFormatter, AksKubernetesAuthProvider, Cluster, ClusterContext, ContainerCard, CronJobsAccordions, CustomResources, DEFAULT_FORMATTER_NAME, DetectedErrorsContext, EksClusterLinksFormatter, ErrorList, ErrorPanel, ErrorReporting, Events, EventsContent, FixDialog, GkeClusterLinksFormatter, GoogleKubernetesAuthProvider, GroupedResponsesContext, HorizontalPodAutoscalerDrawer, IngressesAccordions, JobsAccordions, KubernetesAuthProviders, KubernetesBackendClient, KubernetesClusterLinkFormatter, KubernetesDrawer, KubernetesProxyClient, KubernetesStructuredMetadataTableDrawer, LinkErrorPanel, ManifestYaml, OidcKubernetesAuthProvider, OpenshiftClusterLinksFormatter, PendingPodContent, PodDrawer, PodExecTerminal, PodExecTerminalDialog, PodLogs, PodLogsDialog, PodMetricsContext, PodNamesWithErrorsContext, PodNamesWithMetricsContext, PodsTable, READY_COLUMNS, RESOURCE_COLUMNS, RancherClusterLinksFormatter, ResourceUtilization, ServerSideKubernetesAuthProvider, ServicesAccordions, StandardClusterLinksFormatter, getDefaultFormatters, kubernetesApiRef, kubernetesAuthProvidersApiRef, kubernetesClusterLinkFormatterApiRef, kubernetesProxyApiRef, useCustomResources, useEvents, useIsPodExecTerminalEnabled, useIsPodExecTerminalSupported, useKubernetesObjects, useMatchingErrors, usePodLogs, usePodMetrics };
3306
3320
  //# sourceMappingURL=index.esm.js.map