@backstage/plugin-kubernetes 0.4.14 → 0.4.18
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 +50 -0
- package/dist/index.d.ts +12 -2
- package/dist/index.esm.js +827 -793
- package/dist/index.esm.js.map +1 -1
- package/package.json +15 -15
package/dist/index.esm.js
CHANGED
|
@@ -3,14 +3,15 @@ import * as React from 'react';
|
|
|
3
3
|
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
|
-
import { Typography, Chip, makeStyles, createStyles, Button, Drawer,
|
|
7
|
-
import { WarningPanel, SubvalueCell, StatusError, StatusOK, StatusAborted, Button as Button$1, CodeSnippet, StructuredMetadataTable,
|
|
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';
|
|
8
|
+
import EmptyStateImage from './assets/emptystate.svg';
|
|
8
9
|
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
|
9
10
|
import Close from '@material-ui/icons/Close';
|
|
10
11
|
import OpenInNewIcon from '@material-ui/icons/OpenInNew';
|
|
11
12
|
import { withStyles } from '@material-ui/core/styles';
|
|
12
13
|
import jsYaml from 'js-yaml';
|
|
13
|
-
import
|
|
14
|
+
import { useInterval } from 'react-use';
|
|
14
15
|
import lodash from 'lodash';
|
|
15
16
|
import PauseIcon from '@material-ui/icons/Pause';
|
|
16
17
|
import ErrorOutlineIcon from '@material-ui/icons/ErrorOutline';
|
|
@@ -142,6 +143,7 @@ const kubernetesPlugin = createPlugin({
|
|
|
142
143
|
}
|
|
143
144
|
});
|
|
144
145
|
const EntityKubernetesContent = kubernetesPlugin.provide(createRoutableExtension({
|
|
146
|
+
name: "EntityKubernetesContent",
|
|
145
147
|
component: () => Promise.resolve().then(function () { return Router$1; }).then((m) => m.Router),
|
|
146
148
|
mountPoint: rootCatalogKubernetesRouteRef
|
|
147
149
|
}));
|
|
@@ -171,6 +173,123 @@ const ErrorPanel$1 = ({
|
|
|
171
173
|
variant: "body2"
|
|
172
174
|
}, "Errors: ", errorMessage));
|
|
173
175
|
|
|
176
|
+
const columns$1 = [
|
|
177
|
+
{
|
|
178
|
+
title: "cluster",
|
|
179
|
+
width: "15%",
|
|
180
|
+
render: (detectedError) => detectedError.cluster
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
title: "kind",
|
|
184
|
+
width: "15%",
|
|
185
|
+
render: (detectedError) => detectedError.kind
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
title: "name",
|
|
189
|
+
width: "30%",
|
|
190
|
+
render: (detectedError) => {
|
|
191
|
+
const errorCount = detectedError.names.length;
|
|
192
|
+
if (errorCount === 0) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const displayName = detectedError.names[0];
|
|
196
|
+
const otherErrorCount = errorCount - 1;
|
|
197
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, displayName, " ", otherErrorCount > 0 && /* @__PURE__ */ React.createElement(Chip, {
|
|
198
|
+
label: `+ ${otherErrorCount} other${otherErrorCount > 1 ? "s" : ""}`,
|
|
199
|
+
size: "small"
|
|
200
|
+
}));
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
title: "messages",
|
|
205
|
+
width: "40%",
|
|
206
|
+
render: (detectedError) => /* @__PURE__ */ React.createElement(React.Fragment, null, detectedError.message.map((m, i) => /* @__PURE__ */ React.createElement("div", {
|
|
207
|
+
key: i
|
|
208
|
+
}, m)))
|
|
209
|
+
}
|
|
210
|
+
];
|
|
211
|
+
const sortBySeverity = (a, b) => {
|
|
212
|
+
if (a.severity < b.severity) {
|
|
213
|
+
return 1;
|
|
214
|
+
} else if (b.severity < a.severity) {
|
|
215
|
+
return -1;
|
|
216
|
+
}
|
|
217
|
+
return 0;
|
|
218
|
+
};
|
|
219
|
+
const ErrorEmptyState = () => {
|
|
220
|
+
return /* @__PURE__ */ React.createElement(Grid, {
|
|
221
|
+
container: true,
|
|
222
|
+
justifyContent: "space-around",
|
|
223
|
+
direction: "row",
|
|
224
|
+
alignItems: "center",
|
|
225
|
+
spacing: 2
|
|
226
|
+
}, /* @__PURE__ */ React.createElement(Grid, {
|
|
227
|
+
item: true,
|
|
228
|
+
xs: 4
|
|
229
|
+
}, /* @__PURE__ */ React.createElement(Typography, {
|
|
230
|
+
variant: "h5"
|
|
231
|
+
}, "Nice! There are no errors to report!")), /* @__PURE__ */ React.createElement(Grid, {
|
|
232
|
+
item: true,
|
|
233
|
+
xs: 4
|
|
234
|
+
}, /* @__PURE__ */ React.createElement("img", {
|
|
235
|
+
src: EmptyStateImage,
|
|
236
|
+
alt: "EmptyState",
|
|
237
|
+
"data-testid": "emptyStateImg"
|
|
238
|
+
})));
|
|
239
|
+
};
|
|
240
|
+
const ErrorReporting = ({detectedErrors}) => {
|
|
241
|
+
const errors = Array.from(detectedErrors.values()).flat().sort(sortBySeverity);
|
|
242
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, errors.length === 0 ? /* @__PURE__ */ React.createElement(InfoCard, {
|
|
243
|
+
title: "Error Reporting"
|
|
244
|
+
}, /* @__PURE__ */ React.createElement(ErrorEmptyState, null)) : /* @__PURE__ */ React.createElement(Table, {
|
|
245
|
+
title: "Error Reporting",
|
|
246
|
+
data: errors,
|
|
247
|
+
columns: columns$1,
|
|
248
|
+
options: {paging: true, search: false}
|
|
249
|
+
}));
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const groupResponses = (fetchResponse) => {
|
|
253
|
+
return fetchResponse.reduce((prev, next) => {
|
|
254
|
+
switch (next.type) {
|
|
255
|
+
case "deployments":
|
|
256
|
+
prev.deployments.push(...next.resources);
|
|
257
|
+
break;
|
|
258
|
+
case "pods":
|
|
259
|
+
prev.pods.push(...next.resources);
|
|
260
|
+
break;
|
|
261
|
+
case "replicasets":
|
|
262
|
+
prev.replicaSets.push(...next.resources);
|
|
263
|
+
break;
|
|
264
|
+
case "services":
|
|
265
|
+
prev.services.push(...next.resources);
|
|
266
|
+
break;
|
|
267
|
+
case "configmaps":
|
|
268
|
+
prev.configMaps.push(...next.resources);
|
|
269
|
+
break;
|
|
270
|
+
case "horizontalpodautoscalers":
|
|
271
|
+
prev.horizontalPodAutoscalers.push(...next.resources);
|
|
272
|
+
break;
|
|
273
|
+
case "ingresses":
|
|
274
|
+
prev.ingresses.push(...next.resources);
|
|
275
|
+
break;
|
|
276
|
+
case "customresources":
|
|
277
|
+
prev.customResources.push(...next.resources);
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
return prev;
|
|
281
|
+
}, {
|
|
282
|
+
pods: [],
|
|
283
|
+
replicaSets: [],
|
|
284
|
+
deployments: [],
|
|
285
|
+
services: [],
|
|
286
|
+
configMaps: [],
|
|
287
|
+
horizontalPodAutoscalers: [],
|
|
288
|
+
ingresses: [],
|
|
289
|
+
customResources: []
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
|
|
174
293
|
const imageChips = (pod) => {
|
|
175
294
|
var _a, _b;
|
|
176
295
|
const containerStatuses2 = (_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : [];
|
|
@@ -242,851 +361,760 @@ const renderCondition = (condition) => {
|
|
|
242
361
|
return [condition.type, /* @__PURE__ */ React__default.createElement(StatusAborted, null)];
|
|
243
362
|
};
|
|
244
363
|
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
try {
|
|
267
|
-
requestBody = await kubernetesAuthProvidersApi.decorateRequestBodyForAuth(authProviderStr, requestBody);
|
|
268
|
-
} catch (e) {
|
|
269
|
-
setError(e.message);
|
|
270
|
-
return;
|
|
364
|
+
const detectErrorsInObjects = (objects, kind, clusterName, errorMappers) => {
|
|
365
|
+
var _a, _b;
|
|
366
|
+
const errors = new Map();
|
|
367
|
+
for (const object of objects) {
|
|
368
|
+
for (const errorMapper of errorMappers) {
|
|
369
|
+
if (errorMapper.errorExists(object)) {
|
|
370
|
+
const message = errorMapper.messageAccessor(object);
|
|
371
|
+
const dedupKey = message.join("");
|
|
372
|
+
const value = errors.get(dedupKey);
|
|
373
|
+
const name = (_b = (_a = object.metadata) == null ? void 0 : _a.name) != null ? _b : "unknown";
|
|
374
|
+
if (value !== void 0) {
|
|
375
|
+
value.names.push(name);
|
|
376
|
+
errors.set(dedupKey, value);
|
|
377
|
+
} else {
|
|
378
|
+
errors.set(dedupKey, {
|
|
379
|
+
cluster: clusterName,
|
|
380
|
+
kind,
|
|
381
|
+
names: [name],
|
|
382
|
+
message,
|
|
383
|
+
severity: errorMapper.severity
|
|
384
|
+
});
|
|
271
385
|
}
|
|
272
386
|
}
|
|
273
|
-
|
|
274
|
-
setKubernetesObjects(await kubernetesApi.getObjectsByEntity(requestBody));
|
|
275
|
-
} catch (e) {
|
|
276
|
-
setError(e.message);
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
})();
|
|
280
|
-
}, [entity.metadata.name, kubernetesApi, kubernetesAuthProvidersApi]);
|
|
281
|
-
return {
|
|
282
|
-
kubernetesObjects,
|
|
283
|
-
error
|
|
284
|
-
};
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
const PodNamesWithErrorsContext = React__default.createContext(new Set());
|
|
288
|
-
|
|
289
|
-
const GroupedResponsesContext = React__default.createContext({
|
|
290
|
-
pods: [],
|
|
291
|
-
replicaSets: [],
|
|
292
|
-
deployments: [],
|
|
293
|
-
services: [],
|
|
294
|
-
configMaps: [],
|
|
295
|
-
horizontalPodAutoscalers: [],
|
|
296
|
-
ingresses: [],
|
|
297
|
-
customResources: []
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
const ClusterContext = React__default.createContext({
|
|
301
|
-
name: ""
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
const kindMappings = {
|
|
305
|
-
deployment: "deployment",
|
|
306
|
-
ingress: "ingress",
|
|
307
|
-
service: "service",
|
|
308
|
-
horizontalpodautoscaler: "deployment"
|
|
309
|
-
};
|
|
310
|
-
function standardFormatter(options) {
|
|
311
|
-
var _a, _b;
|
|
312
|
-
const result = new URL(options.dashboardUrl.href);
|
|
313
|
-
const name = (_a = options.object.metadata) == null ? void 0 : _a.name;
|
|
314
|
-
const namespace = (_b = options.object.metadata) == null ? void 0 : _b.namespace;
|
|
315
|
-
const validKind = kindMappings[options.kind.toLocaleLowerCase("en-US")];
|
|
316
|
-
if (namespace) {
|
|
317
|
-
result.searchParams.set("namespace", namespace);
|
|
318
|
-
}
|
|
319
|
-
if (validKind && name && namespace) {
|
|
320
|
-
result.hash = `/${validKind}/${namespace}/${name}`;
|
|
321
|
-
} else if (namespace) {
|
|
322
|
-
result.hash = "/workloads";
|
|
387
|
+
}
|
|
323
388
|
}
|
|
324
|
-
return
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
function rancherFormatter(_options) {
|
|
328
|
-
throw new Error("Rancher formatter is not yet implemented. Please, contribute!");
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
function openshiftFormatter(_options) {
|
|
332
|
-
throw new Error("OpenShift formatter is not yet implemented. Please, contribute!");
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
function aksFormatter(_options) {
|
|
336
|
-
throw new Error("AKS formatter is not yet implemented. Please, contribute!");
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function eksFormatter(_options) {
|
|
340
|
-
throw new Error("EKS formatter is not yet implemented. Please, contribute!");
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
function gkeFormatter(_options) {
|
|
344
|
-
throw new Error("GKE formatter is not yet implemented. Please, contribute!");
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
const clusterLinksFormatters = {
|
|
348
|
-
standard: standardFormatter,
|
|
349
|
-
rancher: rancherFormatter,
|
|
350
|
-
openshift: openshiftFormatter,
|
|
351
|
-
aks: aksFormatter,
|
|
352
|
-
eks: eksFormatter,
|
|
353
|
-
gke: gkeFormatter
|
|
389
|
+
return Array.from(errors.values());
|
|
354
390
|
};
|
|
355
|
-
const defaultFormatterName = "standard";
|
|
356
|
-
|
|
357
|
-
function formatClusterLink(options) {
|
|
358
|
-
if (!options.dashboardUrl) {
|
|
359
|
-
return void 0;
|
|
360
|
-
}
|
|
361
|
-
if (!options.object) {
|
|
362
|
-
return options.dashboardUrl;
|
|
363
|
-
}
|
|
364
|
-
const app = options.dashboardApp || defaultFormatterName;
|
|
365
|
-
const formatter = clusterLinksFormatters[app];
|
|
366
|
-
if (!formatter) {
|
|
367
|
-
throw new Error(`Could not find Kubernetes dashboard app named '${app}'`);
|
|
368
|
-
}
|
|
369
|
-
const url = formatter({
|
|
370
|
-
dashboardUrl: new URL(options.dashboardUrl),
|
|
371
|
-
object: options.object,
|
|
372
|
-
kind: options.kind
|
|
373
|
-
});
|
|
374
|
-
return `${url.origin}${url.pathname}${url.hash}${url.search}`;
|
|
375
|
-
}
|
|
376
391
|
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
const useDrawerContentStyles = makeStyles((_) => createStyles({
|
|
385
|
-
header: {
|
|
386
|
-
display: "flex",
|
|
387
|
-
flexDirection: "row",
|
|
388
|
-
justifyContent: "space-between"
|
|
389
|
-
},
|
|
390
|
-
errorMessage: {
|
|
391
|
-
marginTop: "1em",
|
|
392
|
-
marginBottom: "1em"
|
|
393
|
-
},
|
|
394
|
-
options: {
|
|
395
|
-
display: "flex",
|
|
396
|
-
flexDirection: "row",
|
|
397
|
-
justifyContent: "space-between"
|
|
398
|
-
},
|
|
399
|
-
icon: {
|
|
400
|
-
fontSize: 20
|
|
401
|
-
},
|
|
402
|
-
content: {
|
|
403
|
-
height: "80%"
|
|
404
|
-
}
|
|
405
|
-
}));
|
|
406
|
-
const PodDrawerButton = withStyles({
|
|
407
|
-
root: {
|
|
408
|
-
padding: "6px 5px"
|
|
409
|
-
},
|
|
410
|
-
label: {
|
|
411
|
-
textTransform: "none"
|
|
412
|
-
}
|
|
413
|
-
})(Button);
|
|
414
|
-
const ErrorPanel = ({cluster, errorMessage}) => /* @__PURE__ */ React__default.createElement(WarningPanel, {
|
|
415
|
-
title: "There was a problem formatting the link to the Kubernetes dashboard",
|
|
416
|
-
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"}.'`
|
|
417
|
-
}, errorMessage && /* @__PURE__ */ React__default.createElement(Typography, {
|
|
418
|
-
variant: "body2"
|
|
419
|
-
}, "Errors: ", errorMessage));
|
|
420
|
-
function replaceNullsWithUndefined(someObj) {
|
|
421
|
-
const replacer = (_, value) => String(value) === "null" || String(value) === "undefined" ? void 0 : value;
|
|
422
|
-
return JSON.parse(JSON.stringify(someObj, replacer));
|
|
423
|
-
}
|
|
424
|
-
function tryFormatClusterLink(options) {
|
|
425
|
-
try {
|
|
426
|
-
return {
|
|
427
|
-
clusterLink: formatClusterLink(options),
|
|
428
|
-
errorMessage: ""
|
|
429
|
-
};
|
|
430
|
-
} catch (err) {
|
|
431
|
-
return {
|
|
432
|
-
clusterLink: "",
|
|
433
|
-
errorMessage: err.message || err.toString()
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
const KubernetesDrawerContent = ({
|
|
438
|
-
toggleDrawer,
|
|
439
|
-
object,
|
|
440
|
-
renderObject,
|
|
441
|
-
kind
|
|
442
|
-
}) => {
|
|
443
|
-
var _a, _b;
|
|
444
|
-
const [isYaml, setIsYaml] = useState(false);
|
|
445
|
-
const classes = useDrawerContentStyles();
|
|
446
|
-
const cluster = useContext(ClusterContext);
|
|
447
|
-
const {clusterLink, errorMessage} = tryFormatClusterLink({
|
|
448
|
-
dashboardUrl: cluster.dashboardUrl,
|
|
449
|
-
dashboardApp: cluster.dashboardApp,
|
|
450
|
-
object,
|
|
451
|
-
kind
|
|
452
|
-
});
|
|
453
|
-
return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement("div", {
|
|
454
|
-
className: classes.header
|
|
455
|
-
}, /* @__PURE__ */ React__default.createElement(Grid, {
|
|
456
|
-
container: true,
|
|
457
|
-
direction: "column",
|
|
458
|
-
justifyContent: "flex-start",
|
|
459
|
-
alignItems: "flex-start"
|
|
460
|
-
}, /* @__PURE__ */ React__default.createElement(Grid, {
|
|
461
|
-
item: true
|
|
462
|
-
}, /* @__PURE__ */ React__default.createElement(Typography, {
|
|
463
|
-
variant: "h5"
|
|
464
|
-
}, (_b = (_a = object.metadata) == null ? void 0 : _a.name) != null ? _b : "unknown name")), /* @__PURE__ */ React__default.createElement(Grid, {
|
|
465
|
-
item: true
|
|
466
|
-
}, /* @__PURE__ */ React__default.createElement(Typography, {
|
|
467
|
-
color: "textSecondary",
|
|
468
|
-
variant: "body1"
|
|
469
|
-
}, kind))), /* @__PURE__ */ React__default.createElement(IconButton, {
|
|
470
|
-
key: "dismiss",
|
|
471
|
-
title: "Close the drawer",
|
|
472
|
-
onClick: (e) => toggleDrawer(e, false),
|
|
473
|
-
color: "inherit"
|
|
474
|
-
}, /* @__PURE__ */ React__default.createElement(Close, {
|
|
475
|
-
className: classes.icon
|
|
476
|
-
}))), errorMessage && /* @__PURE__ */ React__default.createElement("div", {
|
|
477
|
-
className: classes.errorMessage
|
|
478
|
-
}, /* @__PURE__ */ React__default.createElement(ErrorPanel, {
|
|
479
|
-
cluster,
|
|
480
|
-
errorMessage
|
|
481
|
-
})), /* @__PURE__ */ React__default.createElement("div", {
|
|
482
|
-
className: classes.options
|
|
483
|
-
}, /* @__PURE__ */ React__default.createElement("div", null, clusterLink && /* @__PURE__ */ React__default.createElement(Button$1, {
|
|
484
|
-
variant: "outlined",
|
|
485
|
-
color: "primary",
|
|
486
|
-
size: "small",
|
|
487
|
-
to: clusterLink,
|
|
488
|
-
endIcon: /* @__PURE__ */ React__default.createElement(OpenInNewIcon, null)
|
|
489
|
-
}, "Open Kubernetes Dashboard")), /* @__PURE__ */ React__default.createElement(FormControlLabel, {
|
|
490
|
-
control: /* @__PURE__ */ React__default.createElement(Switch, {
|
|
491
|
-
checked: isYaml,
|
|
492
|
-
onChange: (event) => {
|
|
493
|
-
setIsYaml(event.target.checked);
|
|
494
|
-
},
|
|
495
|
-
name: "YAML"
|
|
496
|
-
}),
|
|
497
|
-
label: "YAML"
|
|
498
|
-
})), /* @__PURE__ */ React__default.createElement("div", {
|
|
499
|
-
className: classes.content
|
|
500
|
-
}, isYaml && /* @__PURE__ */ React__default.createElement(CodeSnippet, {
|
|
501
|
-
language: "yaml",
|
|
502
|
-
text: jsYaml.dump(object)
|
|
503
|
-
}), !isYaml && /* @__PURE__ */ React__default.createElement(StructuredMetadataTable, {
|
|
504
|
-
metadata: renderObject(replaceNullsWithUndefined(object))
|
|
505
|
-
})));
|
|
506
|
-
};
|
|
507
|
-
const KubernetesDrawer = ({
|
|
508
|
-
object,
|
|
509
|
-
renderObject,
|
|
510
|
-
kind,
|
|
511
|
-
buttonVariant = "subtitle2",
|
|
512
|
-
expanded = false,
|
|
513
|
-
children
|
|
514
|
-
}) => {
|
|
515
|
-
var _a, _b;
|
|
516
|
-
const [isOpen, setIsOpen] = useState(expanded);
|
|
517
|
-
const classes = useDrawerStyles();
|
|
518
|
-
const toggleDrawer = (e, newValue) => {
|
|
519
|
-
e.stopPropagation();
|
|
520
|
-
setIsOpen(newValue);
|
|
521
|
-
};
|
|
522
|
-
return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(PodDrawerButton, {
|
|
523
|
-
onClick: (e) => toggleDrawer(e, true),
|
|
524
|
-
onFocus: (event) => event.stopPropagation()
|
|
525
|
-
}, children === void 0 ? /* @__PURE__ */ React__default.createElement(Typography, {
|
|
526
|
-
variant: buttonVariant
|
|
527
|
-
}, (_b = (_a = object.metadata) == null ? void 0 : _a.name) != null ? _b : "unknown object") : children), /* @__PURE__ */ React__default.createElement(Drawer, {
|
|
528
|
-
classes: {
|
|
529
|
-
paper: classes.paper
|
|
392
|
+
const podErrorMappers = [
|
|
393
|
+
{
|
|
394
|
+
severity: 5,
|
|
395
|
+
errorExplanation: "status-message",
|
|
396
|
+
errorExists: (pod) => {
|
|
397
|
+
var _a;
|
|
398
|
+
return ((_a = pod.status) == null ? void 0 : _a.message) !== void 0;
|
|
530
399
|
},
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
onClick: (event) => event.stopPropagation()
|
|
535
|
-
}, /* @__PURE__ */ React__default.createElement(KubernetesDrawerContent, {
|
|
536
|
-
kind,
|
|
537
|
-
toggleDrawer,
|
|
538
|
-
object,
|
|
539
|
-
renderObject
|
|
540
|
-
})));
|
|
541
|
-
};
|
|
542
|
-
|
|
543
|
-
const PodDrawer = ({
|
|
544
|
-
pod,
|
|
545
|
-
expanded
|
|
546
|
-
}) => {
|
|
547
|
-
return /* @__PURE__ */ React__default.createElement(KubernetesDrawer, {
|
|
548
|
-
object: pod,
|
|
549
|
-
expanded,
|
|
550
|
-
kind: "Pod",
|
|
551
|
-
renderObject: (podObject) => {
|
|
552
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
553
|
-
const phase = (_b = (_a = podObject.status) == null ? void 0 : _a.phase) != null ? _b : "unknown";
|
|
554
|
-
const ports = (_e = (_d = (_c = podObject.spec) == null ? void 0 : _c.containers) == null ? void 0 : _d.map((c) => {
|
|
555
|
-
return {
|
|
556
|
-
[c.name]: c.ports
|
|
557
|
-
};
|
|
558
|
-
})) != null ? _e : "N/A";
|
|
559
|
-
const conditions = ((_g = (_f = podObject.status) == null ? void 0 : _f.conditions) != null ? _g : []).map(renderCondition).reduce((accum, next) => {
|
|
560
|
-
accum[next[0]] = next[1];
|
|
561
|
-
return accum;
|
|
562
|
-
}, {});
|
|
563
|
-
return {
|
|
564
|
-
images: imageChips(podObject),
|
|
565
|
-
phase,
|
|
566
|
-
"Containers Ready": containersReady(podObject),
|
|
567
|
-
"Total Restarts": totalRestarts(podObject),
|
|
568
|
-
"Container Statuses": containerStatuses(podObject),
|
|
569
|
-
...conditions,
|
|
570
|
-
"Exposed ports": ports
|
|
571
|
-
};
|
|
400
|
+
messageAccessor: (pod) => {
|
|
401
|
+
var _a, _b;
|
|
402
|
+
return [(_b = (_a = pod.status) == null ? void 0 : _a.message) != null ? _b : ""];
|
|
572
403
|
}
|
|
573
|
-
}
|
|
574
|
-
};
|
|
575
|
-
|
|
576
|
-
const columns$1 = [
|
|
404
|
+
},
|
|
577
405
|
{
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
pod
|
|
582
|
-
}
|
|
406
|
+
severity: 4,
|
|
407
|
+
errorExplanation: "containers-restarting",
|
|
408
|
+
errorExists: (pod) => {
|
|
409
|
+
return totalRestarts(pod) > 3;
|
|
410
|
+
},
|
|
411
|
+
messageAccessor: (pod) => {
|
|
412
|
+
var _a, _b;
|
|
413
|
+
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).filter((cs) => cs.restartCount > 0).map((cs) => `container=${cs.name} restarted ${cs.restartCount} times`);
|
|
414
|
+
}
|
|
583
415
|
},
|
|
584
416
|
{
|
|
585
|
-
|
|
586
|
-
|
|
417
|
+
severity: 5,
|
|
418
|
+
errorExplanation: "condition-message-present",
|
|
419
|
+
errorExists: (pod) => {
|
|
587
420
|
var _a, _b;
|
|
588
|
-
return (_b = (_a = pod.status) == null ? void 0 : _a.
|
|
421
|
+
return ((_b = (_a = pod.status) == null ? void 0 : _a.conditions) != null ? _b : []).some((c) => c.message !== void 0);
|
|
422
|
+
},
|
|
423
|
+
messageAccessor: (pod) => {
|
|
424
|
+
var _a, _b;
|
|
425
|
+
return ((_b = (_a = pod.status) == null ? void 0 : _a.conditions) != null ? _b : []).filter((c) => c.message !== void 0).map((c) => {
|
|
426
|
+
var _a2;
|
|
427
|
+
return (_a2 = c.message) != null ? _a2 : "";
|
|
428
|
+
});
|
|
589
429
|
}
|
|
590
430
|
},
|
|
591
431
|
{
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
432
|
+
severity: 6,
|
|
433
|
+
errorExplanation: "container-waiting",
|
|
434
|
+
errorExists: (pod) => {
|
|
435
|
+
var _a, _b;
|
|
436
|
+
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).some((cs) => {
|
|
437
|
+
var _a2, _b2;
|
|
438
|
+
return ((_b2 = (_a2 = cs.state) == null ? void 0 : _a2.waiting) == null ? void 0 : _b2.message) !== void 0;
|
|
439
|
+
});
|
|
440
|
+
},
|
|
441
|
+
messageAccessor: (pod) => {
|
|
442
|
+
var _a, _b;
|
|
443
|
+
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).filter((cs) => {
|
|
444
|
+
var _a2, _b2;
|
|
445
|
+
return ((_b2 = (_a2 = cs.state) == null ? void 0 : _a2.waiting) == null ? void 0 : _b2.message) !== void 0;
|
|
446
|
+
}).map((cs) => {
|
|
447
|
+
var _a2, _b2, _c;
|
|
448
|
+
return (_c = (_b2 = (_a2 = cs.state) == null ? void 0 : _a2.waiting) == null ? void 0 : _b2.message) != null ? _c : "";
|
|
449
|
+
});
|
|
450
|
+
}
|
|
595
451
|
},
|
|
596
452
|
{
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
453
|
+
severity: 4,
|
|
454
|
+
errorExplanation: "container-last-state-error",
|
|
455
|
+
errorExists: (pod) => {
|
|
456
|
+
var _a, _b;
|
|
457
|
+
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).some((cs) => {
|
|
458
|
+
var _a2, _b2, _c;
|
|
459
|
+
return ((_c = (_b2 = (_a2 = cs.lastState) == null ? void 0 : _a2.terminated) == null ? void 0 : _b2.reason) != null ? _c : "") === "Error";
|
|
460
|
+
});
|
|
461
|
+
},
|
|
462
|
+
messageAccessor: (pod) => {
|
|
463
|
+
var _a, _b;
|
|
464
|
+
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).filter((cs) => {
|
|
465
|
+
var _a2, _b2, _c;
|
|
466
|
+
return ((_c = (_b2 = (_a2 = cs.lastState) == null ? void 0 : _a2.terminated) == null ? void 0 : _b2.reason) != null ? _c : "") === "Error";
|
|
467
|
+
}).map((cs) => {
|
|
468
|
+
var _a2, _b2;
|
|
469
|
+
return `container=${cs.name} exited with error code (${(_b2 = (_a2 = cs.lastState) == null ? void 0 : _a2.terminated) == null ? void 0 : _b2.exitCode})`;
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
];
|
|
474
|
+
const detectErrorsInPods = (pods, clusterName) => detectErrorsInObjects(pods, "Pod", clusterName, podErrorMappers);
|
|
475
|
+
|
|
476
|
+
const deploymentErrorMappers = [
|
|
602
477
|
{
|
|
603
|
-
|
|
604
|
-
|
|
478
|
+
severity: 6,
|
|
479
|
+
errorExplanation: "condition-message-present",
|
|
480
|
+
errorExists: (deployment) => {
|
|
481
|
+
var _a, _b;
|
|
482
|
+
return ((_b = (_a = deployment.status) == null ? void 0 : _a.conditions) != null ? _b : []).filter((c) => c.status === "False").some((c) => c.message !== void 0);
|
|
483
|
+
},
|
|
484
|
+
messageAccessor: (deployment) => {
|
|
485
|
+
var _a, _b;
|
|
486
|
+
return ((_b = (_a = deployment.status) == null ? void 0 : _a.conditions) != null ? _b : []).filter((c) => c.status === "False").filter((c) => c.message !== void 0).map((c) => {
|
|
487
|
+
var _a2;
|
|
488
|
+
return (_a2 = c.message) != null ? _a2 : "";
|
|
489
|
+
});
|
|
490
|
+
}
|
|
605
491
|
}
|
|
606
492
|
];
|
|
607
|
-
const
|
|
608
|
-
const tableStyle = {
|
|
609
|
-
minWidth: "0",
|
|
610
|
-
width: "100%"
|
|
611
|
-
};
|
|
612
|
-
return /* @__PURE__ */ React__default.createElement("div", {
|
|
613
|
-
style: tableStyle
|
|
614
|
-
}, /* @__PURE__ */ React__default.createElement(Table, {
|
|
615
|
-
options: {paging: true, search: false},
|
|
616
|
-
data: pods,
|
|
617
|
-
columns: columns$1
|
|
618
|
-
}));
|
|
619
|
-
};
|
|
493
|
+
const detectErrorsInDeployments = (deployments, clusterName) => detectErrorsInObjects(deployments, "Deployment", clusterName, deploymentErrorMappers);
|
|
620
494
|
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
return accum;
|
|
635
|
-
}, {});
|
|
636
|
-
return {
|
|
637
|
-
strategy: (_d = (_c = deploymentObj.spec) == null ? void 0 : _c.strategy) != null ? _d : "???",
|
|
638
|
-
minReadySeconds: (_f = (_e = deploymentObj.spec) == null ? void 0 : _e.minReadySeconds) != null ? _f : "???",
|
|
639
|
-
progressDeadlineSeconds: (_h = (_g = deploymentObj.spec) == null ? void 0 : _g.progressDeadlineSeconds) != null ? _h : "???",
|
|
640
|
-
...conditions
|
|
641
|
-
};
|
|
495
|
+
const hpaErrorMappers = [
|
|
496
|
+
{
|
|
497
|
+
severity: 8,
|
|
498
|
+
errorExplanation: "hpa-max-current-replicas",
|
|
499
|
+
errorExists: (hpa) => {
|
|
500
|
+
var _a, _b, _c;
|
|
501
|
+
return ((_b = (_a = hpa.spec) == null ? void 0 : _a.maxReplicas) != null ? _b : -1) === ((_c = hpa.status) == null ? void 0 : _c.currentReplicas);
|
|
502
|
+
},
|
|
503
|
+
messageAccessor: (hpa) => {
|
|
504
|
+
var _a, _b, _c;
|
|
505
|
+
return [
|
|
506
|
+
`Current number of replicas (${(_a = hpa.status) == null ? void 0 : _a.currentReplicas}) is equal to the configured max number of replicas (${(_c = (_b = hpa.spec) == null ? void 0 : _b.maxReplicas) != null ? _c : -1})`
|
|
507
|
+
];
|
|
642
508
|
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
}, "Deployment"))));
|
|
509
|
+
}
|
|
510
|
+
];
|
|
511
|
+
const detectErrorsInHpa = (hpas, clusterName) => detectErrorsInObjects(hpas, "HorizontalPodAutoscaler", clusterName, hpaErrorMappers);
|
|
512
|
+
|
|
513
|
+
const detectErrors = (objects) => {
|
|
514
|
+
const errors = new Map();
|
|
515
|
+
for (const clusterResponse of objects.items) {
|
|
516
|
+
let clusterErrors = [];
|
|
517
|
+
const groupedResponses = groupResponses(clusterResponse.resources);
|
|
518
|
+
clusterErrors = clusterErrors.concat(detectErrorsInPods(groupedResponses.pods, clusterResponse.cluster.name));
|
|
519
|
+
clusterErrors = clusterErrors.concat(detectErrorsInDeployments(groupedResponses.deployments, clusterResponse.cluster.name));
|
|
520
|
+
clusterErrors = clusterErrors.concat(detectErrorsInHpa(groupedResponses.horizontalPodAutoscalers, clusterResponse.cluster.name));
|
|
521
|
+
errors.set(clusterResponse.cluster.name, clusterErrors);
|
|
522
|
+
}
|
|
523
|
+
return errors;
|
|
659
524
|
};
|
|
660
525
|
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
return
|
|
673
|
-
targetCPUUtilizationPercentage: (_a = hpaObject.spec) == null ? void 0 : _a.targetCPUUtilizationPercentage,
|
|
674
|
-
currentCPUUtilizationPercentage: (_b = hpaObject.status) == null ? void 0 : _b.currentCPUUtilizationPercentage,
|
|
675
|
-
minReplicas: (_c = hpaObject.spec) == null ? void 0 : _c.minReplicas,
|
|
676
|
-
maxReplicas: (_d = hpaObject.spec) == null ? void 0 : _d.maxReplicas,
|
|
677
|
-
currentReplicas: (_e = hpaObject.status) == null ? void 0 : _e.currentReplicas,
|
|
678
|
-
desiredReplicas: (_f = hpaObject.status) == null ? void 0 : _f.desiredReplicas
|
|
679
|
-
};
|
|
526
|
+
const useKubernetesObjects = (entity, intervalMs = 1e4) => {
|
|
527
|
+
const kubernetesApi = useApi(kubernetesApiRef);
|
|
528
|
+
const kubernetesAuthProvidersApi = useApi(kubernetesAuthProvidersApiRef);
|
|
529
|
+
const [kubernetesObjects, setKubernetesObjects] = useState(void 0);
|
|
530
|
+
const [error, setError] = useState(void 0);
|
|
531
|
+
const getObjects = async () => {
|
|
532
|
+
let clusters = [];
|
|
533
|
+
try {
|
|
534
|
+
clusters = await kubernetesApi.getClusters();
|
|
535
|
+
} catch (e) {
|
|
536
|
+
setError(e.message);
|
|
537
|
+
return;
|
|
680
538
|
}
|
|
681
|
-
|
|
539
|
+
const authProviders = [
|
|
540
|
+
...new Set(clusters.map((c) => c.authProvider))
|
|
541
|
+
];
|
|
542
|
+
let requestBody = {
|
|
543
|
+
entity
|
|
544
|
+
};
|
|
545
|
+
for (const authProviderStr of authProviders) {
|
|
546
|
+
try {
|
|
547
|
+
requestBody = await kubernetesAuthProvidersApi.decorateRequestBodyForAuth(authProviderStr, requestBody);
|
|
548
|
+
} catch (e) {
|
|
549
|
+
setError(e.message);
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
try {
|
|
554
|
+
setKubernetesObjects(await kubernetesApi.getObjectsByEntity(requestBody));
|
|
555
|
+
} catch (e) {
|
|
556
|
+
setError(e.message);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
useEffect(() => {
|
|
561
|
+
getObjects();
|
|
562
|
+
}, [entity.metadata.name, kubernetesApi, kubernetesAuthProvidersApi]);
|
|
563
|
+
useInterval(() => {
|
|
564
|
+
getObjects();
|
|
565
|
+
}, intervalMs);
|
|
566
|
+
return {
|
|
567
|
+
kubernetesObjects,
|
|
568
|
+
error
|
|
569
|
+
};
|
|
682
570
|
};
|
|
683
571
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
572
|
+
const PodNamesWithErrorsContext = React__default.createContext(new Set());
|
|
573
|
+
|
|
574
|
+
const GroupedResponsesContext = React__default.createContext({
|
|
575
|
+
pods: [],
|
|
576
|
+
replicaSets: [],
|
|
577
|
+
deployments: [],
|
|
578
|
+
services: [],
|
|
579
|
+
configMaps: [],
|
|
580
|
+
horizontalPodAutoscalers: [],
|
|
581
|
+
ingresses: [],
|
|
582
|
+
customResources: []
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
const ClusterContext = React__default.createContext({
|
|
586
|
+
name: ""
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
const kindMappings$1 = {
|
|
590
|
+
deployment: "deployment",
|
|
591
|
+
ingress: "ingress",
|
|
592
|
+
service: "service",
|
|
593
|
+
horizontalpodautoscaler: "deployment"
|
|
594
|
+
};
|
|
595
|
+
function standardFormatter(options) {
|
|
596
|
+
var _a, _b;
|
|
597
|
+
const result = new URL(options.dashboardUrl.href);
|
|
598
|
+
const name = (_a = options.object.metadata) == null ? void 0 : _a.name;
|
|
599
|
+
const namespace = (_b = options.object.metadata) == null ? void 0 : _b.namespace;
|
|
600
|
+
const validKind = kindMappings$1[options.kind.toLocaleLowerCase("en-US")];
|
|
601
|
+
if (namespace) {
|
|
602
|
+
result.searchParams.set("namespace", namespace);
|
|
603
|
+
}
|
|
604
|
+
if (validKind && name && namespace) {
|
|
605
|
+
result.hash = `/${validKind}/${namespace}/${name}`;
|
|
606
|
+
} else if (namespace) {
|
|
607
|
+
result.hash = "/workloads";
|
|
608
|
+
}
|
|
609
|
+
return result;
|
|
692
610
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
611
|
+
|
|
612
|
+
const kindMappings = {
|
|
613
|
+
deployment: "apps.deployment",
|
|
614
|
+
ingress: "networking.k8s.io.ingress",
|
|
615
|
+
service: "service",
|
|
616
|
+
horizontalpodautoscaler: "autoscaling.horizontalpodautoscaler"
|
|
697
617
|
};
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
618
|
+
function rancherFormatter(options) {
|
|
619
|
+
var _a, _b;
|
|
620
|
+
const result = new URL(options.dashboardUrl.href);
|
|
621
|
+
const name = (_a = options.object.metadata) == null ? void 0 : _a.name;
|
|
622
|
+
const namespace = (_b = options.object.metadata) == null ? void 0 : _b.namespace;
|
|
623
|
+
const validKind = kindMappings[options.kind.toLocaleLowerCase("en-US")];
|
|
624
|
+
if (validKind && name && namespace) {
|
|
625
|
+
result.pathname += `explorer/${validKind}/${namespace}/${name}`;
|
|
626
|
+
} else if (namespace) {
|
|
627
|
+
result.pathname += "explorer/workload";
|
|
628
|
+
}
|
|
629
|
+
return result;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function openshiftFormatter(_options) {
|
|
633
|
+
throw new Error("OpenShift formatter is not yet implemented. Please, contribute!");
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function aksFormatter(_options) {
|
|
637
|
+
throw new Error("AKS formatter is not yet implemented. Please, contribute!");
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function eksFormatter(_options) {
|
|
641
|
+
throw new Error("EKS formatter is not yet implemented. Please, contribute!");
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function gkeFormatter(_options) {
|
|
645
|
+
throw new Error("GKE formatter is not yet implemented. Please, contribute!");
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const clusterLinksFormatters = {
|
|
649
|
+
standard: standardFormatter,
|
|
650
|
+
rancher: rancherFormatter,
|
|
651
|
+
openshift: openshiftFormatter,
|
|
652
|
+
aks: aksFormatter,
|
|
653
|
+
eks: eksFormatter,
|
|
654
|
+
gke: gkeFormatter
|
|
703
655
|
};
|
|
656
|
+
const defaultFormatterName = "standard";
|
|
704
657
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
658
|
+
function formatClusterLink(options) {
|
|
659
|
+
if (!options.dashboardUrl) {
|
|
660
|
+
return void 0;
|
|
661
|
+
}
|
|
662
|
+
if (!options.object) {
|
|
663
|
+
return options.dashboardUrl;
|
|
664
|
+
}
|
|
665
|
+
const app = options.dashboardApp || defaultFormatterName;
|
|
666
|
+
const formatter = clusterLinksFormatters[app];
|
|
667
|
+
if (!formatter) {
|
|
668
|
+
throw new Error(`Could not find Kubernetes dashboard app named '${app}'`);
|
|
669
|
+
}
|
|
670
|
+
const url = formatter({
|
|
671
|
+
dashboardUrl: new URL(options.dashboardUrl),
|
|
672
|
+
object: options.object,
|
|
673
|
+
kind: options.kind
|
|
674
|
+
});
|
|
675
|
+
return `${url.origin}${url.pathname}${url.hash}${url.search}`;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
const useDrawerStyles = makeStyles((theme) => createStyles({
|
|
679
|
+
paper: {
|
|
680
|
+
width: "50%",
|
|
681
|
+
justifyContent: "space-between",
|
|
682
|
+
padding: theme.spacing(2.5)
|
|
683
|
+
}
|
|
684
|
+
}));
|
|
685
|
+
const useDrawerContentStyles = makeStyles((_) => createStyles({
|
|
686
|
+
header: {
|
|
687
|
+
display: "flex",
|
|
688
|
+
flexDirection: "row",
|
|
689
|
+
justifyContent: "space-between"
|
|
690
|
+
},
|
|
691
|
+
errorMessage: {
|
|
692
|
+
marginTop: "1em",
|
|
693
|
+
marginBottom: "1em"
|
|
694
|
+
},
|
|
695
|
+
options: {
|
|
696
|
+
display: "flex",
|
|
697
|
+
flexDirection: "row",
|
|
698
|
+
justifyContent: "space-between"
|
|
699
|
+
},
|
|
700
|
+
icon: {
|
|
701
|
+
fontSize: 20
|
|
702
|
+
},
|
|
703
|
+
content: {
|
|
704
|
+
height: "80%"
|
|
705
|
+
}
|
|
706
|
+
}));
|
|
707
|
+
const PodDrawerButton = withStyles({
|
|
708
|
+
root: {
|
|
709
|
+
padding: "6px 5px"
|
|
710
|
+
},
|
|
711
|
+
label: {
|
|
712
|
+
textTransform: "none"
|
|
713
|
+
}
|
|
714
|
+
})(Button);
|
|
715
|
+
const ErrorPanel = ({cluster, errorMessage}) => /* @__PURE__ */ React__default.createElement(WarningPanel, {
|
|
716
|
+
title: "There was a problem formatting the link to the Kubernetes dashboard",
|
|
717
|
+
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"}.'`
|
|
718
|
+
}, errorMessage && /* @__PURE__ */ React__default.createElement(Typography, {
|
|
719
|
+
variant: "body2"
|
|
720
|
+
}, "Errors: ", errorMessage));
|
|
721
|
+
function replaceNullsWithUndefined(someObj) {
|
|
722
|
+
const replacer = (_, value) => String(value) === "null" || String(value) === "undefined" ? void 0 : value;
|
|
723
|
+
return JSON.parse(JSON.stringify(someObj, replacer));
|
|
724
|
+
}
|
|
725
|
+
function tryFormatClusterLink(options) {
|
|
726
|
+
try {
|
|
727
|
+
return {
|
|
728
|
+
clusterLink: formatClusterLink(options),
|
|
729
|
+
errorMessage: ""
|
|
730
|
+
};
|
|
731
|
+
} catch (err) {
|
|
732
|
+
return {
|
|
733
|
+
clusterLink: "",
|
|
734
|
+
errorMessage: err.message || err.toString()
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
const KubernetesDrawerContent = ({
|
|
739
|
+
toggleDrawer,
|
|
740
|
+
object,
|
|
741
|
+
renderObject,
|
|
742
|
+
kind
|
|
743
|
+
}) => {
|
|
744
|
+
var _a, _b;
|
|
745
|
+
const [isYaml, setIsYaml] = useState(false);
|
|
746
|
+
const classes = useDrawerContentStyles();
|
|
747
|
+
const cluster = useContext(ClusterContext);
|
|
748
|
+
const {clusterLink, errorMessage} = tryFormatClusterLink({
|
|
749
|
+
dashboardUrl: cluster.dashboardUrl,
|
|
750
|
+
dashboardApp: cluster.dashboardApp,
|
|
751
|
+
object,
|
|
752
|
+
kind
|
|
753
|
+
});
|
|
754
|
+
return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement("div", {
|
|
755
|
+
className: classes.header
|
|
733
756
|
}, /* @__PURE__ */ React__default.createElement(Grid, {
|
|
734
|
-
item: true,
|
|
735
757
|
container: true,
|
|
736
758
|
direction: "column",
|
|
737
759
|
justifyContent: "flex-start",
|
|
738
|
-
alignItems: "flex-start"
|
|
739
|
-
spacing: 0
|
|
760
|
+
alignItems: "flex-start"
|
|
740
761
|
}, /* @__PURE__ */ React__default.createElement(Grid, {
|
|
741
762
|
item: true
|
|
742
763
|
}, /* @__PURE__ */ React__default.createElement(Typography, {
|
|
743
|
-
variant: "
|
|
744
|
-
},
|
|
745
|
-
item: true
|
|
746
|
-
}, /* @__PURE__ */ React__default.createElement(Typography, {
|
|
747
|
-
variant: "subtitle2"
|
|
748
|
-
}, "current CPU usage:", " ", (_f = (_e = hpa.status) == null ? void 0 : _e.currentCPUUtilizationPercentage) != null ? _f : "?", "%")), /* @__PURE__ */ React__default.createElement(Grid, {
|
|
764
|
+
variant: "h5"
|
|
765
|
+
}, (_b = (_a = object.metadata) == null ? void 0 : _a.name) != null ? _b : "unknown name")), /* @__PURE__ */ React__default.createElement(Grid, {
|
|
749
766
|
item: true
|
|
750
767
|
}, /* @__PURE__ */ React__default.createElement(Typography, {
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
768
|
+
color: "textSecondary",
|
|
769
|
+
variant: "body1"
|
|
770
|
+
}, kind))), /* @__PURE__ */ React__default.createElement(IconButton, {
|
|
771
|
+
key: "dismiss",
|
|
772
|
+
title: "Close the drawer",
|
|
773
|
+
onClick: (e) => toggleDrawer(e, false),
|
|
774
|
+
color: "inherit"
|
|
775
|
+
}, /* @__PURE__ */ React__default.createElement(Close, {
|
|
776
|
+
className: classes.icon
|
|
777
|
+
}))), errorMessage && /* @__PURE__ */ React__default.createElement("div", {
|
|
778
|
+
className: classes.errorMessage
|
|
779
|
+
}, /* @__PURE__ */ React__default.createElement(ErrorPanel, {
|
|
780
|
+
cluster,
|
|
781
|
+
errorMessage
|
|
782
|
+
})), /* @__PURE__ */ React__default.createElement("div", {
|
|
783
|
+
className: classes.options
|
|
784
|
+
}, /* @__PURE__ */ React__default.createElement("div", null, clusterLink && /* @__PURE__ */ React__default.createElement(Button$1, {
|
|
785
|
+
variant: "outlined",
|
|
786
|
+
color: "primary",
|
|
787
|
+
size: "small",
|
|
788
|
+
to: clusterLink,
|
|
789
|
+
endIcon: /* @__PURE__ */ React__default.createElement(OpenInNewIcon, null)
|
|
790
|
+
}, "Open Kubernetes Dashboard")), /* @__PURE__ */ React__default.createElement(FormControlLabel, {
|
|
791
|
+
control: /* @__PURE__ */ React__default.createElement(Switch, {
|
|
792
|
+
checked: isYaml,
|
|
793
|
+
onChange: (event) => {
|
|
794
|
+
setIsYaml(event.target.checked);
|
|
795
|
+
},
|
|
796
|
+
name: "YAML"
|
|
797
|
+
}),
|
|
798
|
+
label: "YAML"
|
|
799
|
+
})), /* @__PURE__ */ React__default.createElement("div", {
|
|
800
|
+
className: classes.content
|
|
801
|
+
}, isYaml && /* @__PURE__ */ React__default.createElement(CodeSnippet, {
|
|
802
|
+
language: "yaml",
|
|
803
|
+
text: jsYaml.dump(object)
|
|
804
|
+
}), !isYaml && /* @__PURE__ */ React__default.createElement(StructuredMetadataTable, {
|
|
805
|
+
metadata: renderObject(replaceNullsWithUndefined(object))
|
|
806
|
+
})));
|
|
764
807
|
};
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
808
|
+
const KubernetesDrawer = ({
|
|
809
|
+
object,
|
|
810
|
+
renderObject,
|
|
811
|
+
kind,
|
|
812
|
+
buttonVariant = "subtitle2",
|
|
813
|
+
expanded = false,
|
|
814
|
+
children
|
|
769
815
|
}) => {
|
|
770
|
-
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
816
|
+
var _a, _b;
|
|
817
|
+
const [isOpen, setIsOpen] = useState(expanded);
|
|
818
|
+
const classes = useDrawerStyles();
|
|
819
|
+
const toggleDrawer = (e, newValue) => {
|
|
820
|
+
e.stopPropagation();
|
|
821
|
+
setIsOpen(newValue);
|
|
822
|
+
};
|
|
823
|
+
return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(PodDrawerButton, {
|
|
824
|
+
onClick: (e) => toggleDrawer(e, true),
|
|
825
|
+
onFocus: (event) => event.stopPropagation()
|
|
826
|
+
}, children === void 0 ? /* @__PURE__ */ React__default.createElement(Typography, {
|
|
827
|
+
variant: buttonVariant
|
|
828
|
+
}, (_b = (_a = object.metadata) == null ? void 0 : _a.name) != null ? _b : "unknown object") : children), /* @__PURE__ */ React__default.createElement(Drawer, {
|
|
829
|
+
classes: {
|
|
830
|
+
paper: classes.paper
|
|
831
|
+
},
|
|
832
|
+
anchor: "right",
|
|
833
|
+
open: isOpen,
|
|
834
|
+
onClose: (e) => toggleDrawer(e, false),
|
|
835
|
+
onClick: (event) => event.stopPropagation()
|
|
836
|
+
}, /* @__PURE__ */ React__default.createElement(KubernetesDrawerContent, {
|
|
837
|
+
kind,
|
|
838
|
+
toggleDrawer,
|
|
839
|
+
object,
|
|
840
|
+
renderObject
|
|
786
841
|
})));
|
|
787
842
|
};
|
|
788
|
-
const DeploymentsAccordions = ({}) => {
|
|
789
|
-
const groupedResponses = useContext(GroupedResponsesContext);
|
|
790
|
-
return /* @__PURE__ */ React__default.createElement(Grid, {
|
|
791
|
-
container: true,
|
|
792
|
-
direction: "column",
|
|
793
|
-
justifyContent: "flex-start",
|
|
794
|
-
alignItems: "flex-start"
|
|
795
|
-
}, groupedResponses.deployments.map((deployment, i) => {
|
|
796
|
-
var _a;
|
|
797
|
-
return /* @__PURE__ */ React__default.createElement(Grid, {
|
|
798
|
-
container: true,
|
|
799
|
-
item: true,
|
|
800
|
-
key: i,
|
|
801
|
-
xs: true
|
|
802
|
-
}, /* @__PURE__ */ React__default.createElement(Grid, {
|
|
803
|
-
item: true,
|
|
804
|
-
xs: true
|
|
805
|
-
}, /* @__PURE__ */ React__default.createElement(DeploymentAccordion, {
|
|
806
|
-
matchingHpa: getMatchingHpa((_a = deployment.metadata) == null ? void 0 : _a.name, "deployment", groupedResponses.horizontalPodAutoscalers),
|
|
807
|
-
ownedPods: getOwnedPodsThroughReplicaSets(deployment, groupedResponses.replicaSets, groupedResponses.pods),
|
|
808
|
-
deployment
|
|
809
|
-
})));
|
|
810
|
-
}));
|
|
811
|
-
};
|
|
812
843
|
|
|
813
|
-
const
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
width: "40%",
|
|
843
|
-
render: (detectedError) => /* @__PURE__ */ React.createElement(React.Fragment, null, detectedError.message.map((m, i) => /* @__PURE__ */ React.createElement("div", {
|
|
844
|
-
key: i
|
|
845
|
-
}, m)))
|
|
846
|
-
}
|
|
847
|
-
];
|
|
848
|
-
const sortBySeverity = (a, b) => {
|
|
849
|
-
if (a.severity < b.severity) {
|
|
850
|
-
return 1;
|
|
851
|
-
} else if (b.severity < a.severity) {
|
|
852
|
-
return -1;
|
|
853
|
-
}
|
|
854
|
-
return 0;
|
|
855
|
-
};
|
|
856
|
-
const ErrorEmptyState = () => {
|
|
857
|
-
return /* @__PURE__ */ React.createElement(Grid, {
|
|
858
|
-
container: true,
|
|
859
|
-
justifyContent: "space-around",
|
|
860
|
-
direction: "row",
|
|
861
|
-
alignItems: "center",
|
|
862
|
-
spacing: 2
|
|
863
|
-
}, /* @__PURE__ */ React.createElement(Grid, {
|
|
864
|
-
item: true,
|
|
865
|
-
xs: 4
|
|
866
|
-
}, /* @__PURE__ */ React.createElement(Typography, {
|
|
867
|
-
variant: "h5"
|
|
868
|
-
}, "Nice! There are no errors to report!")), /* @__PURE__ */ React.createElement(Grid, {
|
|
869
|
-
item: true,
|
|
870
|
-
xs: 4
|
|
871
|
-
}, /* @__PURE__ */ React.createElement("img", {
|
|
872
|
-
src: EmptyStateImage,
|
|
873
|
-
alt: "EmptyState",
|
|
874
|
-
"data-testid": "emptyStateImg"
|
|
875
|
-
})));
|
|
876
|
-
};
|
|
877
|
-
const ErrorReporting = ({detectedErrors}) => {
|
|
878
|
-
const errors = Array.from(detectedErrors.values()).flat().sort(sortBySeverity);
|
|
879
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, errors.length === 0 ? /* @__PURE__ */ React.createElement(InfoCard, {
|
|
880
|
-
title: "Error Reporting"
|
|
881
|
-
}, /* @__PURE__ */ React.createElement(ErrorEmptyState, null)) : /* @__PURE__ */ React.createElement(Table, {
|
|
882
|
-
title: "Error Reporting",
|
|
883
|
-
data: errors,
|
|
884
|
-
columns,
|
|
885
|
-
options: {paging: true, search: false}
|
|
886
|
-
}));
|
|
887
|
-
};
|
|
888
|
-
|
|
889
|
-
const groupResponses = (fetchResponse) => {
|
|
890
|
-
return fetchResponse.reduce((prev, next) => {
|
|
891
|
-
switch (next.type) {
|
|
892
|
-
case "deployments":
|
|
893
|
-
prev.deployments.push(...next.resources);
|
|
894
|
-
break;
|
|
895
|
-
case "pods":
|
|
896
|
-
prev.pods.push(...next.resources);
|
|
897
|
-
break;
|
|
898
|
-
case "replicasets":
|
|
899
|
-
prev.replicaSets.push(...next.resources);
|
|
900
|
-
break;
|
|
901
|
-
case "services":
|
|
902
|
-
prev.services.push(...next.resources);
|
|
903
|
-
break;
|
|
904
|
-
case "configmaps":
|
|
905
|
-
prev.configMaps.push(...next.resources);
|
|
906
|
-
break;
|
|
907
|
-
case "horizontalpodautoscalers":
|
|
908
|
-
prev.horizontalPodAutoscalers.push(...next.resources);
|
|
909
|
-
break;
|
|
910
|
-
case "ingresses":
|
|
911
|
-
prev.ingresses.push(...next.resources);
|
|
912
|
-
break;
|
|
913
|
-
case "customresources":
|
|
914
|
-
prev.customResources.push(...next.resources);
|
|
915
|
-
break;
|
|
844
|
+
const PodDrawer = ({
|
|
845
|
+
pod,
|
|
846
|
+
expanded
|
|
847
|
+
}) => {
|
|
848
|
+
return /* @__PURE__ */ React__default.createElement(KubernetesDrawer, {
|
|
849
|
+
object: pod,
|
|
850
|
+
expanded,
|
|
851
|
+
kind: "Pod",
|
|
852
|
+
renderObject: (podObject) => {
|
|
853
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
854
|
+
const phase = (_b = (_a = podObject.status) == null ? void 0 : _a.phase) != null ? _b : "unknown";
|
|
855
|
+
const ports = (_e = (_d = (_c = podObject.spec) == null ? void 0 : _c.containers) == null ? void 0 : _d.map((c) => {
|
|
856
|
+
return {
|
|
857
|
+
[c.name]: c.ports
|
|
858
|
+
};
|
|
859
|
+
})) != null ? _e : "N/A";
|
|
860
|
+
const conditions = ((_g = (_f = podObject.status) == null ? void 0 : _f.conditions) != null ? _g : []).map(renderCondition).reduce((accum, next) => {
|
|
861
|
+
accum[next[0]] = next[1];
|
|
862
|
+
return accum;
|
|
863
|
+
}, {});
|
|
864
|
+
return {
|
|
865
|
+
images: imageChips(podObject),
|
|
866
|
+
phase,
|
|
867
|
+
"Containers Ready": containersReady(podObject),
|
|
868
|
+
"Total Restarts": totalRestarts(podObject),
|
|
869
|
+
"Container Statuses": containerStatuses(podObject),
|
|
870
|
+
...conditions,
|
|
871
|
+
"Exposed ports": ports
|
|
872
|
+
};
|
|
916
873
|
}
|
|
917
|
-
return prev;
|
|
918
|
-
}, {
|
|
919
|
-
pods: [],
|
|
920
|
-
replicaSets: [],
|
|
921
|
-
deployments: [],
|
|
922
|
-
services: [],
|
|
923
|
-
configMaps: [],
|
|
924
|
-
horizontalPodAutoscalers: [],
|
|
925
|
-
ingresses: [],
|
|
926
|
-
customResources: []
|
|
927
874
|
});
|
|
928
875
|
};
|
|
929
876
|
|
|
930
|
-
const
|
|
931
|
-
var _a, _b;
|
|
932
|
-
const errors = new Map();
|
|
933
|
-
for (const object of objects) {
|
|
934
|
-
for (const errorMapper of errorMappers) {
|
|
935
|
-
if (errorMapper.errorExists(object)) {
|
|
936
|
-
const message = errorMapper.messageAccessor(object);
|
|
937
|
-
const dedupKey = message.join("");
|
|
938
|
-
const value = errors.get(dedupKey);
|
|
939
|
-
const name = (_b = (_a = object.metadata) == null ? void 0 : _a.name) != null ? _b : "unknown";
|
|
940
|
-
if (value !== void 0) {
|
|
941
|
-
value.names.push(name);
|
|
942
|
-
errors.set(dedupKey, value);
|
|
943
|
-
} else {
|
|
944
|
-
errors.set(dedupKey, {
|
|
945
|
-
cluster: clusterName,
|
|
946
|
-
kind,
|
|
947
|
-
names: [name],
|
|
948
|
-
message,
|
|
949
|
-
severity: errorMapper.severity
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
return Array.from(errors.values());
|
|
956
|
-
};
|
|
957
|
-
|
|
958
|
-
const podErrorMappers = [
|
|
959
|
-
{
|
|
960
|
-
severity: 5,
|
|
961
|
-
errorExplanation: "status-message",
|
|
962
|
-
errorExists: (pod) => {
|
|
963
|
-
var _a;
|
|
964
|
-
return ((_a = pod.status) == null ? void 0 : _a.message) !== void 0;
|
|
965
|
-
},
|
|
966
|
-
messageAccessor: (pod) => {
|
|
967
|
-
var _a, _b;
|
|
968
|
-
return [(_b = (_a = pod.status) == null ? void 0 : _a.message) != null ? _b : ""];
|
|
969
|
-
}
|
|
970
|
-
},
|
|
971
|
-
{
|
|
972
|
-
severity: 4,
|
|
973
|
-
errorExplanation: "containers-restarting",
|
|
974
|
-
errorExists: (pod) => {
|
|
975
|
-
return totalRestarts(pod) > 3;
|
|
976
|
-
},
|
|
977
|
-
messageAccessor: (pod) => {
|
|
978
|
-
var _a, _b;
|
|
979
|
-
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).filter((cs) => cs.restartCount > 0).map((cs) => `container=${cs.name} restarted ${cs.restartCount} times`);
|
|
980
|
-
}
|
|
981
|
-
},
|
|
982
|
-
{
|
|
983
|
-
severity: 5,
|
|
984
|
-
errorExplanation: "condition-message-present",
|
|
985
|
-
errorExists: (pod) => {
|
|
986
|
-
var _a, _b;
|
|
987
|
-
return ((_b = (_a = pod.status) == null ? void 0 : _a.conditions) != null ? _b : []).some((c) => c.message !== void 0);
|
|
988
|
-
},
|
|
989
|
-
messageAccessor: (pod) => {
|
|
990
|
-
var _a, _b;
|
|
991
|
-
return ((_b = (_a = pod.status) == null ? void 0 : _a.conditions) != null ? _b : []).filter((c) => c.message !== void 0).map((c) => {
|
|
992
|
-
var _a2;
|
|
993
|
-
return (_a2 = c.message) != null ? _a2 : "";
|
|
994
|
-
});
|
|
995
|
-
}
|
|
996
|
-
},
|
|
997
|
-
{
|
|
998
|
-
severity: 6,
|
|
999
|
-
errorExplanation: "container-waiting",
|
|
1000
|
-
errorExists: (pod) => {
|
|
1001
|
-
var _a, _b;
|
|
1002
|
-
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).some((cs) => {
|
|
1003
|
-
var _a2, _b2;
|
|
1004
|
-
return ((_b2 = (_a2 = cs.state) == null ? void 0 : _a2.waiting) == null ? void 0 : _b2.message) !== void 0;
|
|
1005
|
-
});
|
|
1006
|
-
},
|
|
1007
|
-
messageAccessor: (pod) => {
|
|
1008
|
-
var _a, _b;
|
|
1009
|
-
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).filter((cs) => {
|
|
1010
|
-
var _a2, _b2;
|
|
1011
|
-
return ((_b2 = (_a2 = cs.state) == null ? void 0 : _a2.waiting) == null ? void 0 : _b2.message) !== void 0;
|
|
1012
|
-
}).map((cs) => {
|
|
1013
|
-
var _a2, _b2, _c;
|
|
1014
|
-
return (_c = (_b2 = (_a2 = cs.state) == null ? void 0 : _a2.waiting) == null ? void 0 : _b2.message) != null ? _c : "";
|
|
1015
|
-
});
|
|
1016
|
-
}
|
|
1017
|
-
},
|
|
1018
|
-
{
|
|
1019
|
-
severity: 4,
|
|
1020
|
-
errorExplanation: "container-last-state-error",
|
|
1021
|
-
errorExists: (pod) => {
|
|
1022
|
-
var _a, _b;
|
|
1023
|
-
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).some((cs) => {
|
|
1024
|
-
var _a2, _b2, _c;
|
|
1025
|
-
return ((_c = (_b2 = (_a2 = cs.lastState) == null ? void 0 : _a2.terminated) == null ? void 0 : _b2.reason) != null ? _c : "") === "Error";
|
|
1026
|
-
});
|
|
1027
|
-
},
|
|
1028
|
-
messageAccessor: (pod) => {
|
|
1029
|
-
var _a, _b;
|
|
1030
|
-
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).filter((cs) => {
|
|
1031
|
-
var _a2, _b2, _c;
|
|
1032
|
-
return ((_c = (_b2 = (_a2 = cs.lastState) == null ? void 0 : _a2.terminated) == null ? void 0 : _b2.reason) != null ? _c : "") === "Error";
|
|
1033
|
-
}).map((cs) => {
|
|
1034
|
-
var _a2, _b2;
|
|
1035
|
-
return `container=${cs.name} exited with error code (${(_b2 = (_a2 = cs.lastState) == null ? void 0 : _a2.terminated) == null ? void 0 : _b2.exitCode})`;
|
|
1036
|
-
});
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
];
|
|
1040
|
-
const detectErrorsInPods = (pods, clusterName) => detectErrorsInObjects(pods, "Pod", clusterName, podErrorMappers);
|
|
1041
|
-
|
|
1042
|
-
const deploymentErrorMappers = [
|
|
877
|
+
const columns = [
|
|
1043
878
|
{
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
879
|
+
title: "name",
|
|
880
|
+
highlight: true,
|
|
881
|
+
render: (pod) => /* @__PURE__ */ React__default.createElement(PodDrawer, {
|
|
882
|
+
pod
|
|
883
|
+
})
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
title: "phase",
|
|
887
|
+
render: (pod) => {
|
|
1051
888
|
var _a, _b;
|
|
1052
|
-
return (
|
|
1053
|
-
var _a2;
|
|
1054
|
-
return (_a2 = c.message) != null ? _a2 : "";
|
|
1055
|
-
});
|
|
889
|
+
return (_b = (_a = pod.status) == null ? void 0 : _a.phase) != null ? _b : "unknown";
|
|
1056
890
|
}
|
|
891
|
+
},
|
|
892
|
+
{
|
|
893
|
+
title: "containers ready",
|
|
894
|
+
align: "center",
|
|
895
|
+
render: containersReady
|
|
896
|
+
},
|
|
897
|
+
{
|
|
898
|
+
title: "total restarts",
|
|
899
|
+
align: "center",
|
|
900
|
+
render: totalRestarts,
|
|
901
|
+
type: "numeric"
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
title: "status",
|
|
905
|
+
render: containerStatuses
|
|
1057
906
|
}
|
|
1058
907
|
];
|
|
1059
|
-
const
|
|
908
|
+
const PodsTable = ({pods}) => {
|
|
909
|
+
const tableStyle = {
|
|
910
|
+
minWidth: "0",
|
|
911
|
+
width: "100%"
|
|
912
|
+
};
|
|
913
|
+
return /* @__PURE__ */ React__default.createElement("div", {
|
|
914
|
+
style: tableStyle
|
|
915
|
+
}, /* @__PURE__ */ React__default.createElement(Table, {
|
|
916
|
+
options: {paging: true, search: false},
|
|
917
|
+
data: pods,
|
|
918
|
+
columns
|
|
919
|
+
}));
|
|
920
|
+
};
|
|
1060
921
|
|
|
1061
|
-
const
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
]
|
|
922
|
+
const DeploymentDrawer = ({
|
|
923
|
+
deployment,
|
|
924
|
+
expanded
|
|
925
|
+
}) => {
|
|
926
|
+
var _a, _b, _c;
|
|
927
|
+
const namespace = (_a = deployment.metadata) == null ? void 0 : _a.namespace;
|
|
928
|
+
return /* @__PURE__ */ React__default.createElement(KubernetesDrawer, {
|
|
929
|
+
object: deployment,
|
|
930
|
+
expanded,
|
|
931
|
+
kind: "Deployment",
|
|
932
|
+
renderObject: (deploymentObj) => {
|
|
933
|
+
var _a2, _b2, _c2, _d, _e, _f, _g, _h;
|
|
934
|
+
const conditions = ((_b2 = (_a2 = deploymentObj.status) == null ? void 0 : _a2.conditions) != null ? _b2 : []).map(renderCondition).reduce((accum, next) => {
|
|
935
|
+
accum[next[0]] = next[1];
|
|
936
|
+
return accum;
|
|
937
|
+
}, {});
|
|
938
|
+
return {
|
|
939
|
+
strategy: (_d = (_c2 = deploymentObj.spec) == null ? void 0 : _c2.strategy) != null ? _d : "???",
|
|
940
|
+
minReadySeconds: (_f = (_e = deploymentObj.spec) == null ? void 0 : _e.minReadySeconds) != null ? _f : "???",
|
|
941
|
+
progressDeadlineSeconds: (_h = (_g = deploymentObj.spec) == null ? void 0 : _g.progressDeadlineSeconds) != null ? _h : "???",
|
|
942
|
+
...conditions
|
|
943
|
+
};
|
|
1074
944
|
}
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
|
|
945
|
+
}, /* @__PURE__ */ React__default.createElement(Grid, {
|
|
946
|
+
container: true,
|
|
947
|
+
direction: "column",
|
|
948
|
+
justifyContent: "flex-start",
|
|
949
|
+
alignItems: "flex-start",
|
|
950
|
+
spacing: 0
|
|
951
|
+
}, /* @__PURE__ */ React__default.createElement(Grid, {
|
|
952
|
+
item: true
|
|
953
|
+
}, /* @__PURE__ */ React__default.createElement(Typography, {
|
|
954
|
+
variant: "h5"
|
|
955
|
+
}, (_c = (_b = deployment.metadata) == null ? void 0 : _b.name) != null ? _c : "unknown object")), /* @__PURE__ */ React__default.createElement(Grid, {
|
|
956
|
+
item: true
|
|
957
|
+
}, /* @__PURE__ */ React__default.createElement(Typography, {
|
|
958
|
+
color: "textSecondary",
|
|
959
|
+
variant: "body1"
|
|
960
|
+
}, "Deployment")), namespace && /* @__PURE__ */ React__default.createElement(Grid, {
|
|
961
|
+
item: true
|
|
962
|
+
}, /* @__PURE__ */ React__default.createElement(Chip, {
|
|
963
|
+
size: "small",
|
|
964
|
+
label: `namespace: ${namespace}`
|
|
965
|
+
}))));
|
|
966
|
+
};
|
|
1078
967
|
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
968
|
+
const HorizontalPodAutoscalerDrawer = ({
|
|
969
|
+
hpa,
|
|
970
|
+
expanded,
|
|
971
|
+
children
|
|
972
|
+
}) => {
|
|
973
|
+
return /* @__PURE__ */ React__default.createElement(KubernetesDrawer, {
|
|
974
|
+
kind: "HorizontalPodAutoscaler",
|
|
975
|
+
object: hpa,
|
|
976
|
+
expanded,
|
|
977
|
+
renderObject: (hpaObject) => {
|
|
978
|
+
var _a, _b, _c, _d, _e, _f;
|
|
979
|
+
return {
|
|
980
|
+
targetCPUUtilizationPercentage: (_a = hpaObject.spec) == null ? void 0 : _a.targetCPUUtilizationPercentage,
|
|
981
|
+
currentCPUUtilizationPercentage: (_b = hpaObject.status) == null ? void 0 : _b.currentCPUUtilizationPercentage,
|
|
982
|
+
minReplicas: (_c = hpaObject.spec) == null ? void 0 : _c.minReplicas,
|
|
983
|
+
maxReplicas: (_d = hpaObject.spec) == null ? void 0 : _d.maxReplicas,
|
|
984
|
+
currentReplicas: (_e = hpaObject.status) == null ? void 0 : _e.currentReplicas,
|
|
985
|
+
desiredReplicas: (_f = hpaObject.status) == null ? void 0 : _f.desiredReplicas
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
}, children);
|
|
989
|
+
};
|
|
990
|
+
|
|
991
|
+
function getOwnedResources(potentialOwner, possiblyOwned) {
|
|
992
|
+
return possiblyOwned.filter((p) => {
|
|
993
|
+
var _a, _b, _c;
|
|
994
|
+
return (_c = (_b = (_a = p.metadata) == null ? void 0 : _a.ownerReferences) == null ? void 0 : _b.some((o) => {
|
|
995
|
+
var _a2;
|
|
996
|
+
return o.uid === ((_a2 = potentialOwner.metadata) == null ? void 0 : _a2.uid);
|
|
997
|
+
})) != null ? _c : false;
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
const getOwnedPodsThroughReplicaSets = (potentialOwner, replicaSets, pods) => {
|
|
1001
|
+
return getOwnedResources(potentialOwner, replicaSets.filter((rs) => rs.status && rs.status.replicas > 0)).reduce((accum, rs) => {
|
|
1002
|
+
return accum.concat(getOwnedResources(rs, pods));
|
|
1003
|
+
}, []);
|
|
1004
|
+
};
|
|
1005
|
+
const getMatchingHpa = (ownerName, ownerKind, hpas) => {
|
|
1006
|
+
return hpas.find((hpa) => {
|
|
1007
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1008
|
+
return ((_c = (_b = (_a = hpa.spec) == null ? void 0 : _a.scaleTargetRef) == null ? void 0 : _b.kind) != null ? _c : "").toLocaleLowerCase("en-US") === ownerKind.toLocaleLowerCase("en-US") && ((_f = (_e = (_d = hpa.spec) == null ? void 0 : _d.scaleTargetRef) == null ? void 0 : _e.name) != null ? _f : "") === (ownerName != null ? ownerName : "unknown-deployment");
|
|
1009
|
+
});
|
|
1010
|
+
};
|
|
1011
|
+
|
|
1012
|
+
const DeploymentSummary = ({
|
|
1013
|
+
deployment,
|
|
1014
|
+
numberOfCurrentPods,
|
|
1015
|
+
numberOfPodsWithErrors,
|
|
1016
|
+
hpa
|
|
1017
|
+
}) => {
|
|
1018
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1019
|
+
return /* @__PURE__ */ React__default.createElement(Grid, {
|
|
1020
|
+
container: true,
|
|
1021
|
+
direction: "row",
|
|
1022
|
+
justifyContent: "flex-start",
|
|
1023
|
+
alignItems: "center"
|
|
1024
|
+
}, /* @__PURE__ */ React__default.createElement(Grid, {
|
|
1025
|
+
xs: 3,
|
|
1026
|
+
item: true
|
|
1027
|
+
}, /* @__PURE__ */ React__default.createElement(DeploymentDrawer, {
|
|
1028
|
+
deployment
|
|
1029
|
+
})), /* @__PURE__ */ React__default.createElement(Grid, {
|
|
1030
|
+
item: true,
|
|
1031
|
+
xs: 1
|
|
1032
|
+
}, /* @__PURE__ */ React__default.createElement(Divider, {
|
|
1033
|
+
style: {height: "5em"},
|
|
1034
|
+
orientation: "vertical"
|
|
1035
|
+
})), hpa && /* @__PURE__ */ React__default.createElement(Grid, {
|
|
1036
|
+
item: true,
|
|
1037
|
+
xs: 3
|
|
1038
|
+
}, /* @__PURE__ */ React__default.createElement(HorizontalPodAutoscalerDrawer, {
|
|
1039
|
+
hpa
|
|
1040
|
+
}, /* @__PURE__ */ React__default.createElement(Grid, {
|
|
1041
|
+
item: true,
|
|
1042
|
+
container: true,
|
|
1043
|
+
direction: "column",
|
|
1044
|
+
justifyContent: "flex-start",
|
|
1045
|
+
alignItems: "flex-start",
|
|
1046
|
+
spacing: 0
|
|
1047
|
+
}, /* @__PURE__ */ React__default.createElement(Grid, {
|
|
1048
|
+
item: true
|
|
1049
|
+
}, /* @__PURE__ */ React__default.createElement(Typography, {
|
|
1050
|
+
variant: "subtitle2"
|
|
1051
|
+
}, "min replicas ", (_b = (_a = hpa.spec) == null ? void 0 : _a.minReplicas) != null ? _b : "?", " / max replicas", " ", (_d = (_c = hpa.spec) == null ? void 0 : _c.maxReplicas) != null ? _d : "?")), /* @__PURE__ */ React__default.createElement(Grid, {
|
|
1052
|
+
item: true
|
|
1053
|
+
}, /* @__PURE__ */ React__default.createElement(Typography, {
|
|
1054
|
+
variant: "subtitle2"
|
|
1055
|
+
}, "current CPU usage:", " ", (_f = (_e = hpa.status) == null ? void 0 : _e.currentCPUUtilizationPercentage) != null ? _f : "?", "%")), /* @__PURE__ */ React__default.createElement(Grid, {
|
|
1056
|
+
item: true
|
|
1057
|
+
}, /* @__PURE__ */ React__default.createElement(Typography, {
|
|
1058
|
+
variant: "subtitle2"
|
|
1059
|
+
}, "target CPU usage:", " ", (_h = (_g = hpa.spec) == null ? void 0 : _g.targetCPUUtilizationPercentage) != null ? _h : "?", "%"))))), /* @__PURE__ */ React__default.createElement(Grid, {
|
|
1060
|
+
item: true,
|
|
1061
|
+
container: true,
|
|
1062
|
+
xs: 3,
|
|
1063
|
+
direction: "column",
|
|
1064
|
+
justifyContent: "flex-start",
|
|
1065
|
+
alignItems: "flex-start"
|
|
1066
|
+
}, /* @__PURE__ */ React__default.createElement(Grid, {
|
|
1067
|
+
item: true
|
|
1068
|
+
}, /* @__PURE__ */ React__default.createElement(StatusOK, null, numberOfCurrentPods, " pods")), /* @__PURE__ */ React__default.createElement(Grid, {
|
|
1069
|
+
item: true
|
|
1070
|
+
}, numberOfPodsWithErrors > 0 ? /* @__PURE__ */ React__default.createElement(StatusError, null, numberOfPodsWithErrors, " pod", numberOfPodsWithErrors > 1 ? "s" : "", " with errors") : /* @__PURE__ */ React__default.createElement(StatusOK, null, "No pods with errors"))));
|
|
1071
|
+
};
|
|
1072
|
+
const DeploymentAccordion = ({
|
|
1073
|
+
deployment,
|
|
1074
|
+
ownedPods,
|
|
1075
|
+
matchingHpa
|
|
1076
|
+
}) => {
|
|
1077
|
+
const podNamesWithErrors = useContext(PodNamesWithErrorsContext);
|
|
1078
|
+
const podsWithErrors = ownedPods.filter((p) => {
|
|
1079
|
+
var _a, _b;
|
|
1080
|
+
return podNamesWithErrors.has((_b = (_a = p.metadata) == null ? void 0 : _a.name) != null ? _b : "");
|
|
1081
|
+
});
|
|
1082
|
+
return /* @__PURE__ */ React__default.createElement(Accordion, {
|
|
1083
|
+
TransitionProps: {unmountOnExit: true}
|
|
1084
|
+
}, /* @__PURE__ */ React__default.createElement(AccordionSummary, {
|
|
1085
|
+
expandIcon: /* @__PURE__ */ React__default.createElement(ExpandMoreIcon, null)
|
|
1086
|
+
}, /* @__PURE__ */ React__default.createElement(DeploymentSummary, {
|
|
1087
|
+
deployment,
|
|
1088
|
+
numberOfCurrentPods: ownedPods.length,
|
|
1089
|
+
numberOfPodsWithErrors: podsWithErrors.length,
|
|
1090
|
+
hpa: matchingHpa
|
|
1091
|
+
})), /* @__PURE__ */ React__default.createElement(AccordionDetails, null, /* @__PURE__ */ React__default.createElement(PodsTable, {
|
|
1092
|
+
pods: ownedPods
|
|
1093
|
+
})));
|
|
1094
|
+
};
|
|
1095
|
+
const DeploymentsAccordions = ({}) => {
|
|
1096
|
+
const groupedResponses = useContext(GroupedResponsesContext);
|
|
1097
|
+
return /* @__PURE__ */ React__default.createElement(Grid, {
|
|
1098
|
+
container: true,
|
|
1099
|
+
direction: "column",
|
|
1100
|
+
justifyContent: "flex-start",
|
|
1101
|
+
alignItems: "flex-start"
|
|
1102
|
+
}, groupedResponses.deployments.map((deployment, i) => {
|
|
1103
|
+
var _a;
|
|
1104
|
+
return /* @__PURE__ */ React__default.createElement(Grid, {
|
|
1105
|
+
container: true,
|
|
1106
|
+
item: true,
|
|
1107
|
+
key: i,
|
|
1108
|
+
xs: true
|
|
1109
|
+
}, /* @__PURE__ */ React__default.createElement(Grid, {
|
|
1110
|
+
item: true,
|
|
1111
|
+
xs: true
|
|
1112
|
+
}, /* @__PURE__ */ React__default.createElement(DeploymentAccordion, {
|
|
1113
|
+
matchingHpa: getMatchingHpa((_a = deployment.metadata) == null ? void 0 : _a.name, "deployment", groupedResponses.horizontalPodAutoscalers),
|
|
1114
|
+
ownedPods: getOwnedPodsThroughReplicaSets(deployment, groupedResponses.replicaSets, groupedResponses.pods),
|
|
1115
|
+
deployment
|
|
1116
|
+
})));
|
|
1117
|
+
}));
|
|
1090
1118
|
};
|
|
1091
1119
|
|
|
1092
1120
|
const IngressDrawer = ({
|
|
@@ -1689,6 +1717,7 @@ const Cluster = ({clusterObjects, podsWithErrors}) => {
|
|
|
1689
1717
|
item: true
|
|
1690
1718
|
}, /* @__PURE__ */ React__default.createElement(ServicesAccordions, null))))))));
|
|
1691
1719
|
};
|
|
1720
|
+
|
|
1692
1721
|
const KubernetesContent = ({entity}) => {
|
|
1693
1722
|
var _a;
|
|
1694
1723
|
const {kubernetesObjects, error} = useKubernetesObjects(entity);
|
|
@@ -1765,6 +1794,10 @@ const KubernetesContent = ({entity}) => {
|
|
|
1765
1794
|
|
|
1766
1795
|
const KUBERNETES_ANNOTATION = "backstage.io/kubernetes-id";
|
|
1767
1796
|
const KUBERNETES_LABEL_SELECTOR_QUERY_ANNOTATION = "backstage.io/kubernetes-label-selector";
|
|
1797
|
+
const isKubernetesAvailable = (entity) => {
|
|
1798
|
+
var _a, _b;
|
|
1799
|
+
return Boolean((_a = entity.metadata.annotations) == null ? void 0 : _a[KUBERNETES_ANNOTATION]) || Boolean((_b = entity.metadata.annotations) == null ? void 0 : _b[KUBERNETES_LABEL_SELECTOR_QUERY_ANNOTATION]);
|
|
1800
|
+
};
|
|
1768
1801
|
const Router = (_props) => {
|
|
1769
1802
|
var _a, _b;
|
|
1770
1803
|
const {entity} = useEntity();
|
|
@@ -1789,8 +1822,9 @@ const Router = (_props) => {
|
|
|
1789
1822
|
|
|
1790
1823
|
var Router$1 = /*#__PURE__*/Object.freeze({
|
|
1791
1824
|
__proto__: null,
|
|
1825
|
+
isKubernetesAvailable: isKubernetesAvailable,
|
|
1792
1826
|
Router: Router
|
|
1793
1827
|
});
|
|
1794
1828
|
|
|
1795
|
-
export { EntityKubernetesContent, KubernetesAuthProviders, Router, clusterLinksFormatters, formatClusterLink, kubernetesAuthProvidersApiRef, kubernetesPlugin, kubernetesPlugin as plugin };
|
|
1829
|
+
export { EntityKubernetesContent, KubernetesAuthProviders, Router, clusterLinksFormatters, formatClusterLink, isKubernetesAvailable, kubernetesApiRef, kubernetesAuthProvidersApiRef, kubernetesPlugin, kubernetesPlugin as plugin };
|
|
1796
1830
|
//# sourceMappingURL=index.esm.js.map
|