@backstage/plugin-kubernetes 0.4.17 → 0.4.21

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