@backstage/plugin-kubernetes 0.7.10-next.1 → 0.8.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/dist/index.d.ts +92 -46
- package/dist/index.esm.js +273 -225
- package/dist/index.esm.js.map +1 -1
- package/package.json +12 -10
package/dist/index.esm.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { stringifyEntityRef } from '@backstage/catalog-model';
|
|
2
|
+
import { NotFoundError } from '@backstage/errors';
|
|
2
3
|
import { createApiRef, createRouteRef, createPlugin, createApiFactory, discoveryApiRef, identityApiRef, gitlabAuthApiRef, googleAuthApiRef, microsoftAuthApiRef, oktaAuthApiRef, oneloginAuthApiRef, createRoutableExtension, useApi } from '@backstage/core-plugin-api';
|
|
3
4
|
import * as React from 'react';
|
|
4
5
|
import React__default, { Fragment, useCallback, useState, useContext } from 'react';
|
|
@@ -6,6 +7,8 @@ import { useEntity } from '@backstage/plugin-catalog-react';
|
|
|
6
7
|
import { Routes, Route } from 'react-router-dom';
|
|
7
8
|
import { Typography, Chip, makeStyles, createStyles, Button, Drawer, Grid, IconButton, FormControlLabel, Switch, Accordion, AccordionSummary, AccordionDetails, Stepper, Step, StepLabel } from '@material-ui/core';
|
|
8
9
|
import { WarningPanel, Table, StatusOK, SubvalueCell, StatusError, StatusAborted, LinkButton, CodeSnippet, StructuredMetadataTable, StatusPending, Page, Content, Progress, MissingAnnotationEmptyState } from '@backstage/core-components';
|
|
10
|
+
import lodash from 'lodash';
|
|
11
|
+
import { DateTime } from 'luxon';
|
|
9
12
|
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
|
10
13
|
import Close from '@material-ui/icons/Close';
|
|
11
14
|
import OpenInNewIcon from '@material-ui/icons/OpenInNew';
|
|
@@ -14,10 +17,8 @@ import jsYaml from 'js-yaml';
|
|
|
14
17
|
import useInterval from 'react-use/lib/useInterval';
|
|
15
18
|
import useAsyncRetry from 'react-use/lib/useAsyncRetry';
|
|
16
19
|
import cronstrue from 'cronstrue';
|
|
17
|
-
import lodash from 'lodash';
|
|
18
20
|
import PauseIcon from '@material-ui/icons/Pause';
|
|
19
21
|
import ErrorOutlineIcon from '@material-ui/icons/ErrorOutline';
|
|
20
|
-
import { DateTime } from 'luxon';
|
|
21
22
|
import Typography$1 from '@material-ui/core/Typography';
|
|
22
23
|
import EmptyStateImage from './assets/emptystate.svg';
|
|
23
24
|
|
|
@@ -25,6 +26,7 @@ class KubernetesBackendClient {
|
|
|
25
26
|
constructor(options) {
|
|
26
27
|
this.discoveryApi = options.discoveryApi;
|
|
27
28
|
this.identityApi = options.identityApi;
|
|
29
|
+
this.kubernetesAuthProvidersApi = options.kubernetesAuthProvidersApi;
|
|
28
30
|
}
|
|
29
31
|
async handleResponse(response) {
|
|
30
32
|
if (!response.ok) {
|
|
@@ -54,6 +56,18 @@ class KubernetesBackendClient {
|
|
|
54
56
|
});
|
|
55
57
|
return this.handleResponse(response);
|
|
56
58
|
}
|
|
59
|
+
async getCluster(clusterName) {
|
|
60
|
+
const cluster = await this.getClusters().then(
|
|
61
|
+
(clusters) => clusters.find((c) => c.name === clusterName)
|
|
62
|
+
);
|
|
63
|
+
if (!cluster) {
|
|
64
|
+
throw new NotFoundError(`Cluster ${clusterName} not found`);
|
|
65
|
+
}
|
|
66
|
+
return cluster;
|
|
67
|
+
}
|
|
68
|
+
async getCredentials(authProvider) {
|
|
69
|
+
return await this.kubernetesAuthProvidersApi.getCredentials(authProvider);
|
|
70
|
+
}
|
|
57
71
|
async getObjectsByEntity(requestBody) {
|
|
58
72
|
return await this.postRequired(
|
|
59
73
|
`/services/${requestBody.entity.metadata.name}`,
|
|
@@ -84,6 +98,24 @@ class KubernetesBackendClient {
|
|
|
84
98
|
});
|
|
85
99
|
return (await this.handleResponse(response)).items;
|
|
86
100
|
}
|
|
101
|
+
async proxy(options) {
|
|
102
|
+
var _a;
|
|
103
|
+
const { authProvider } = await this.getCluster(options.clusterName);
|
|
104
|
+
const { token: k8sToken } = await this.getCredentials(authProvider);
|
|
105
|
+
const url = `${await this.discoveryApi.getBaseUrl("kubernetes")}/proxy${options.path}`;
|
|
106
|
+
const identityResponse = await this.identityApi.getCredentials();
|
|
107
|
+
const headers = {
|
|
108
|
+
...(_a = options.init) == null ? void 0 : _a.headers,
|
|
109
|
+
[`Backstage-Kubernetes-Cluster`]: options.clusterName,
|
|
110
|
+
...k8sToken && {
|
|
111
|
+
[`Backstage-Kubernetes-Authorization`]: `Bearer ${k8sToken}`
|
|
112
|
+
},
|
|
113
|
+
...identityResponse.token && {
|
|
114
|
+
Authorization: `Bearer ${identityResponse.token}`
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
return await fetch(url, { ...options.init, headers });
|
|
118
|
+
}
|
|
87
119
|
}
|
|
88
120
|
|
|
89
121
|
const kubernetesApiRef = createApiRef({
|
|
@@ -99,9 +131,7 @@ class GoogleKubernetesAuthProvider {
|
|
|
99
131
|
this.authProvider = authProvider;
|
|
100
132
|
}
|
|
101
133
|
async decorateRequestBodyForAuth(requestBody) {
|
|
102
|
-
const googleAuthToken = await this.
|
|
103
|
-
"https://www.googleapis.com/auth/cloud-platform"
|
|
104
|
-
);
|
|
134
|
+
const googleAuthToken = (await this.getCredentials()).token;
|
|
105
135
|
if ("auth" in requestBody) {
|
|
106
136
|
requestBody.auth.google = googleAuthToken;
|
|
107
137
|
} else {
|
|
@@ -109,12 +139,22 @@ class GoogleKubernetesAuthProvider {
|
|
|
109
139
|
}
|
|
110
140
|
return requestBody;
|
|
111
141
|
}
|
|
142
|
+
async getCredentials() {
|
|
143
|
+
return {
|
|
144
|
+
token: await this.authProvider.getAccessToken(
|
|
145
|
+
"https://www.googleapis.com/auth/cloud-platform"
|
|
146
|
+
)
|
|
147
|
+
};
|
|
148
|
+
}
|
|
112
149
|
}
|
|
113
150
|
|
|
114
151
|
class ServerSideKubernetesAuthProvider {
|
|
115
152
|
async decorateRequestBodyForAuth(requestBody) {
|
|
116
153
|
return requestBody;
|
|
117
154
|
}
|
|
155
|
+
async getCredentials() {
|
|
156
|
+
return {};
|
|
157
|
+
}
|
|
118
158
|
}
|
|
119
159
|
|
|
120
160
|
class OidcKubernetesAuthProvider {
|
|
@@ -123,7 +163,7 @@ class OidcKubernetesAuthProvider {
|
|
|
123
163
|
this.authProvider = authProvider;
|
|
124
164
|
}
|
|
125
165
|
async decorateRequestBodyForAuth(requestBody) {
|
|
126
|
-
const authToken = await this.
|
|
166
|
+
const authToken = (await this.getCredentials()).token;
|
|
127
167
|
const auth = { ...requestBody.auth };
|
|
128
168
|
if (auth.oidc) {
|
|
129
169
|
auth.oidc[this.providerName] = authToken;
|
|
@@ -133,6 +173,11 @@ class OidcKubernetesAuthProvider {
|
|
|
133
173
|
requestBody.auth = auth;
|
|
134
174
|
return requestBody;
|
|
135
175
|
}
|
|
176
|
+
async getCredentials() {
|
|
177
|
+
return {
|
|
178
|
+
token: await this.authProvider.getIdToken()
|
|
179
|
+
};
|
|
180
|
+
}
|
|
136
181
|
}
|
|
137
182
|
|
|
138
183
|
class KubernetesAuthProviders {
|
|
@@ -190,6 +235,20 @@ class KubernetesAuthProviders {
|
|
|
190
235
|
`authProvider "${authProvider}" has no KubernetesAuthProvider defined for it`
|
|
191
236
|
);
|
|
192
237
|
}
|
|
238
|
+
async getCredentials(authProvider) {
|
|
239
|
+
const kubernetesAuthProvider = this.kubernetesAuthProviderMap.get(authProvider);
|
|
240
|
+
if (kubernetesAuthProvider) {
|
|
241
|
+
return await kubernetesAuthProvider.getCredentials();
|
|
242
|
+
}
|
|
243
|
+
if (authProvider.startsWith("oidc.")) {
|
|
244
|
+
throw new Error(
|
|
245
|
+
`KubernetesAuthProviders has no oidcProvider configured for ${authProvider}`
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
throw new Error(
|
|
249
|
+
`authProvider "${authProvider}" has no KubernetesAuthProvider defined for it`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
193
252
|
}
|
|
194
253
|
|
|
195
254
|
const rootCatalogKubernetesRouteRef = createRouteRef({
|
|
@@ -202,9 +261,14 @@ const kubernetesPlugin = createPlugin({
|
|
|
202
261
|
api: kubernetesApiRef,
|
|
203
262
|
deps: {
|
|
204
263
|
discoveryApi: discoveryApiRef,
|
|
205
|
-
identityApi: identityApiRef
|
|
264
|
+
identityApi: identityApiRef,
|
|
265
|
+
kubernetesAuthProvidersApi: kubernetesAuthProvidersApiRef
|
|
206
266
|
},
|
|
207
|
-
factory: ({ discoveryApi, identityApi }) => new KubernetesBackendClient({
|
|
267
|
+
factory: ({ discoveryApi, identityApi, kubernetesAuthProvidersApi }) => new KubernetesBackendClient({
|
|
268
|
+
discoveryApi,
|
|
269
|
+
identityApi,
|
|
270
|
+
kubernetesAuthProvidersApi
|
|
271
|
+
})
|
|
208
272
|
}),
|
|
209
273
|
createApiFactory({
|
|
210
274
|
api: kubernetesAuthProvidersApiRef,
|
|
@@ -270,53 +334,46 @@ const columns = [
|
|
|
270
334
|
{
|
|
271
335
|
title: "cluster",
|
|
272
336
|
width: "10%",
|
|
273
|
-
render: (
|
|
337
|
+
render: (row) => row.clusterName
|
|
274
338
|
},
|
|
275
339
|
{
|
|
276
340
|
title: "namespace",
|
|
277
341
|
width: "10%",
|
|
278
|
-
render: (
|
|
342
|
+
render: (row) => row.error.sourceRef.namespace
|
|
279
343
|
},
|
|
280
344
|
{
|
|
281
345
|
title: "kind",
|
|
282
346
|
width: "10%",
|
|
283
|
-
render: (
|
|
347
|
+
render: (row) => row.error.sourceRef.kind
|
|
284
348
|
},
|
|
285
349
|
{
|
|
286
350
|
title: "name",
|
|
287
351
|
width: "30%",
|
|
288
|
-
render: (
|
|
289
|
-
|
|
290
|
-
if (errorCount === 0) {
|
|
291
|
-
return null;
|
|
292
|
-
}
|
|
293
|
-
const displayName = detectedError.names[0];
|
|
294
|
-
const otherErrorCount = errorCount - 1;
|
|
295
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, displayName, " ", otherErrorCount > 0 && /* @__PURE__ */ React.createElement(
|
|
296
|
-
Chip,
|
|
297
|
-
{
|
|
298
|
-
label: `+ ${otherErrorCount} other${otherErrorCount > 1 ? "s" : ""}`,
|
|
299
|
-
size: "small"
|
|
300
|
-
}
|
|
301
|
-
));
|
|
352
|
+
render: (row) => {
|
|
353
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, row.error.sourceRef.name, " ");
|
|
302
354
|
}
|
|
303
355
|
},
|
|
304
356
|
{
|
|
305
357
|
title: "messages",
|
|
306
358
|
width: "40%",
|
|
307
|
-
render: (
|
|
359
|
+
render: (row) => row.error.message
|
|
308
360
|
}
|
|
309
361
|
];
|
|
310
362
|
const sortBySeverity = (a, b) => {
|
|
311
|
-
if (a.severity < b.severity) {
|
|
363
|
+
if (a.error.severity < b.error.severity) {
|
|
312
364
|
return 1;
|
|
313
|
-
} else if (b.severity < a.severity) {
|
|
365
|
+
} else if (b.error.severity < a.error.severity) {
|
|
314
366
|
return -1;
|
|
315
367
|
}
|
|
316
368
|
return 0;
|
|
317
369
|
};
|
|
318
370
|
const ErrorReporting = ({ detectedErrors }) => {
|
|
319
|
-
const errors = Array.from(detectedErrors.
|
|
371
|
+
const errors = Array.from(detectedErrors.entries()).flatMap(([clusterName, resourceErrors]) => {
|
|
372
|
+
return resourceErrors.map((e) => ({
|
|
373
|
+
clusterName,
|
|
374
|
+
error: e
|
|
375
|
+
}));
|
|
376
|
+
}).sort(sortBySeverity);
|
|
320
377
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, errors.length !== 0 && /* @__PURE__ */ React.createElement(
|
|
321
378
|
Table,
|
|
322
379
|
{
|
|
@@ -384,6 +441,191 @@ const groupResponses = (fetchResponse) => {
|
|
|
384
441
|
);
|
|
385
442
|
};
|
|
386
443
|
|
|
444
|
+
const detectErrorsInObjects = (objects, errorMappers) => {
|
|
445
|
+
return objects.flatMap((o) => {
|
|
446
|
+
return errorMappers.flatMap((em) => em.detectErrors(o));
|
|
447
|
+
});
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
function isPodReadinessProbeUnready({
|
|
451
|
+
container,
|
|
452
|
+
containerStatus
|
|
453
|
+
}) {
|
|
454
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
455
|
+
if (containerStatus.ready || ((_b = (_a = containerStatus.state) == null ? void 0 : _a.running) == null ? void 0 : _b.startedAt) === void 0 || !container.readinessProbe) {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
const startDateTime = DateTime.fromISO(
|
|
459
|
+
(_d = (_c = containerStatus.state) == null ? void 0 : _c.running) == null ? void 0 : _d.startedAt
|
|
460
|
+
).plus({
|
|
461
|
+
seconds: (_f = (_e = container.readinessProbe) == null ? void 0 : _e.initialDelaySeconds) != null ? _f : 0
|
|
462
|
+
}).plus({
|
|
463
|
+
seconds: ((_h = (_g = container.readinessProbe) == null ? void 0 : _g.periodSeconds) != null ? _h : 0) * ((_j = (_i = container.readinessProbe) == null ? void 0 : _i.failureThreshold) != null ? _j : 0)
|
|
464
|
+
});
|
|
465
|
+
return startDateTime < DateTime.now();
|
|
466
|
+
}
|
|
467
|
+
const podToContainerSpecsAndStatuses = (pod) => {
|
|
468
|
+
var _a, _b, _c, _d;
|
|
469
|
+
const specs = lodash.groupBy((_b = (_a = pod.spec) == null ? void 0 : _a.containers) != null ? _b : [], (value) => value.name);
|
|
470
|
+
const result = [];
|
|
471
|
+
for (const cs of (_d = (_c = pod.status) == null ? void 0 : _c.containerStatuses) != null ? _d : []) {
|
|
472
|
+
const spec = specs[cs.name];
|
|
473
|
+
if (spec.length > 0) {
|
|
474
|
+
result.push({
|
|
475
|
+
container: spec[0],
|
|
476
|
+
containerStatus: cs
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return result;
|
|
481
|
+
};
|
|
482
|
+
const podErrorMappers = [
|
|
483
|
+
{
|
|
484
|
+
detectErrors: (pod) => {
|
|
485
|
+
return podToContainerSpecsAndStatuses(pod).filter(isPodReadinessProbeUnready).map((cs) => {
|
|
486
|
+
var _a, _b, _c, _d;
|
|
487
|
+
return {
|
|
488
|
+
type: "readiness-probe-taking-too-long",
|
|
489
|
+
message: `The container ${cs.container.name} failed to start properly, but is not crashing`,
|
|
490
|
+
severity: 4,
|
|
491
|
+
proposedFix: [],
|
|
492
|
+
// TODO next PR
|
|
493
|
+
sourceRef: {
|
|
494
|
+
name: (_b = (_a = pod.metadata) == null ? void 0 : _a.name) != null ? _b : "unknown pod",
|
|
495
|
+
namespace: (_d = (_c = pod.metadata) == null ? void 0 : _c.namespace) != null ? _d : "unknown namespace",
|
|
496
|
+
kind: "Pod",
|
|
497
|
+
apiGroup: "v1"
|
|
498
|
+
},
|
|
499
|
+
occuranceCount: 1
|
|
500
|
+
};
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
detectErrors: (pod) => {
|
|
506
|
+
var _a, _b;
|
|
507
|
+
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).filter((cs) => {
|
|
508
|
+
var _a2, _b2;
|
|
509
|
+
return ((_b2 = (_a2 = cs.state) == null ? void 0 : _a2.waiting) == null ? void 0 : _b2.message) !== void 0;
|
|
510
|
+
}).map((cs) => {
|
|
511
|
+
var _a2, _b2, _c, _d, _e, _f, _g;
|
|
512
|
+
return {
|
|
513
|
+
type: "container-waiting",
|
|
514
|
+
message: (_c = (_b2 = (_a2 = cs.state) == null ? void 0 : _a2.waiting) == null ? void 0 : _b2.message) != null ? _c : "container waiting",
|
|
515
|
+
severity: 4,
|
|
516
|
+
proposedFix: [],
|
|
517
|
+
// TODO next PR
|
|
518
|
+
sourceRef: {
|
|
519
|
+
name: (_e = (_d = pod.metadata) == null ? void 0 : _d.name) != null ? _e : "unknown pod",
|
|
520
|
+
namespace: (_g = (_f = pod.metadata) == null ? void 0 : _f.namespace) != null ? _g : "unknown namespace",
|
|
521
|
+
kind: "Pod",
|
|
522
|
+
apiGroup: "v1"
|
|
523
|
+
},
|
|
524
|
+
occuranceCount: 1
|
|
525
|
+
};
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
detectErrors: (pod) => {
|
|
531
|
+
var _a, _b;
|
|
532
|
+
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).filter((cs) => cs.restartCount > 0).map((cs) => {
|
|
533
|
+
var _a2, _b2, _c, _d;
|
|
534
|
+
return {
|
|
535
|
+
type: "containers-restarting",
|
|
536
|
+
message: `container=${cs.name} restarted ${cs.restartCount} times`,
|
|
537
|
+
severity: 4,
|
|
538
|
+
proposedFix: [],
|
|
539
|
+
// TODO next PR
|
|
540
|
+
sourceRef: {
|
|
541
|
+
name: (_b2 = (_a2 = pod.metadata) == null ? void 0 : _a2.name) != null ? _b2 : "unknown pod",
|
|
542
|
+
namespace: (_d = (_c = pod.metadata) == null ? void 0 : _c.namespace) != null ? _d : "unknown namespace",
|
|
543
|
+
kind: "Pod",
|
|
544
|
+
apiGroup: "v1"
|
|
545
|
+
},
|
|
546
|
+
occuranceCount: cs.restartCount
|
|
547
|
+
};
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
];
|
|
552
|
+
const detectErrorsInPods = (pods) => detectErrorsInObjects(pods, podErrorMappers);
|
|
553
|
+
|
|
554
|
+
const deploymentErrorMappers = [
|
|
555
|
+
{
|
|
556
|
+
detectErrors: (deployment) => {
|
|
557
|
+
var _a, _b;
|
|
558
|
+
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) => {
|
|
559
|
+
var _a2, _b2, _c, _d, _e;
|
|
560
|
+
return {
|
|
561
|
+
type: "condition-message-present",
|
|
562
|
+
message: (_a2 = c.message) != null ? _a2 : "",
|
|
563
|
+
severity: 6,
|
|
564
|
+
proposedFix: [],
|
|
565
|
+
// TODO next PR
|
|
566
|
+
sourceRef: {
|
|
567
|
+
name: (_c = (_b2 = deployment.metadata) == null ? void 0 : _b2.name) != null ? _c : "unknown hpa",
|
|
568
|
+
namespace: (_e = (_d = deployment.metadata) == null ? void 0 : _d.namespace) != null ? _e : "unknown namespace",
|
|
569
|
+
kind: "Deployment",
|
|
570
|
+
apiGroup: "apps/v1"
|
|
571
|
+
},
|
|
572
|
+
occuranceCount: 1
|
|
573
|
+
};
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
];
|
|
578
|
+
const detectErrorsInDeployments = (deployments) => detectErrorsInObjects(deployments, deploymentErrorMappers);
|
|
579
|
+
|
|
580
|
+
const hpaErrorMappers = [
|
|
581
|
+
{
|
|
582
|
+
detectErrors: (hpa) => {
|
|
583
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
584
|
+
if (((_b = (_a = hpa.spec) == null ? void 0 : _a.maxReplicas) != null ? _b : -1) === ((_c = hpa.status) == null ? void 0 : _c.currentReplicas)) {
|
|
585
|
+
return [
|
|
586
|
+
{
|
|
587
|
+
type: "hpa-max-current-replicas",
|
|
588
|
+
message: `Current number of replicas (${(_d = hpa.status) == null ? void 0 : _d.currentReplicas}) is equal to the configured max number of replicas (${(_f = (_e = hpa.spec) == null ? void 0 : _e.maxReplicas) != null ? _f : -1})`,
|
|
589
|
+
severity: 8,
|
|
590
|
+
proposedFix: [],
|
|
591
|
+
// TODO next PR
|
|
592
|
+
sourceRef: {
|
|
593
|
+
name: (_h = (_g = hpa.metadata) == null ? void 0 : _g.name) != null ? _h : "unknown hpa",
|
|
594
|
+
namespace: (_j = (_i = hpa.metadata) == null ? void 0 : _i.namespace) != null ? _j : "unknown namespace",
|
|
595
|
+
kind: "HorizontalPodAutoscaler",
|
|
596
|
+
apiGroup: "autoscaling/v1"
|
|
597
|
+
},
|
|
598
|
+
occuranceCount: 1
|
|
599
|
+
}
|
|
600
|
+
];
|
|
601
|
+
}
|
|
602
|
+
return [];
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
];
|
|
606
|
+
const detectErrorsInHpa = (hpas) => detectErrorsInObjects(hpas, hpaErrorMappers);
|
|
607
|
+
|
|
608
|
+
const detectErrors = (objects) => {
|
|
609
|
+
const errors = /* @__PURE__ */ new Map();
|
|
610
|
+
for (const clusterResponse of objects.items) {
|
|
611
|
+
let clusterErrors = [];
|
|
612
|
+
const groupedResponses = groupResponses(clusterResponse.resources);
|
|
613
|
+
clusterErrors = clusterErrors.concat(
|
|
614
|
+
detectErrorsInPods(groupedResponses.pods)
|
|
615
|
+
);
|
|
616
|
+
clusterErrors = clusterErrors.concat(
|
|
617
|
+
detectErrorsInDeployments(groupedResponses.deployments)
|
|
618
|
+
);
|
|
619
|
+
clusterErrors = clusterErrors.concat(
|
|
620
|
+
detectErrorsInHpa(
|
|
621
|
+
groupedResponses.horizontalPodAutoscalers
|
|
622
|
+
)
|
|
623
|
+
);
|
|
624
|
+
errors.set(clusterResponse.cluster.name, clusterErrors);
|
|
625
|
+
}
|
|
626
|
+
return errors;
|
|
627
|
+
};
|
|
628
|
+
|
|
387
629
|
const imageChips = (pod) => {
|
|
388
630
|
var _a, _b;
|
|
389
631
|
const containerStatuses2 = (_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : [];
|
|
@@ -507,200 +749,6 @@ const podStatusToMemoryUtil = (podStatus) => {
|
|
|
507
749
|
);
|
|
508
750
|
};
|
|
509
751
|
|
|
510
|
-
const detectErrorsInObjects = (objects, kind, clusterName, errorMappers) => {
|
|
511
|
-
var _a, _b, _c, _d;
|
|
512
|
-
const errors = /* @__PURE__ */ new Map();
|
|
513
|
-
for (const object of objects) {
|
|
514
|
-
for (const errorMapper of errorMappers) {
|
|
515
|
-
if (errorMapper.errorExists(object)) {
|
|
516
|
-
const message = errorMapper.messageAccessor(object);
|
|
517
|
-
const dedupKey = message.join("");
|
|
518
|
-
const value = errors.get(dedupKey);
|
|
519
|
-
const name = (_b = (_a = object.metadata) == null ? void 0 : _a.name) != null ? _b : "unknown";
|
|
520
|
-
const namespace = (_d = (_c = object.metadata) == null ? void 0 : _c.namespace) != null ? _d : "unknown";
|
|
521
|
-
if (value !== void 0) {
|
|
522
|
-
value.names.push(name);
|
|
523
|
-
errors.set(dedupKey, value);
|
|
524
|
-
} else {
|
|
525
|
-
errors.set(dedupKey, {
|
|
526
|
-
cluster: clusterName,
|
|
527
|
-
kind,
|
|
528
|
-
names: [name],
|
|
529
|
-
message,
|
|
530
|
-
severity: errorMapper.severity,
|
|
531
|
-
namespace
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
return Array.from(errors.values());
|
|
538
|
-
};
|
|
539
|
-
|
|
540
|
-
const podErrorMappers = [
|
|
541
|
-
{
|
|
542
|
-
severity: 5,
|
|
543
|
-
errorExplanation: "status-message",
|
|
544
|
-
errorExists: (pod) => {
|
|
545
|
-
var _a;
|
|
546
|
-
return ((_a = pod.status) == null ? void 0 : _a.message) !== void 0;
|
|
547
|
-
},
|
|
548
|
-
messageAccessor: (pod) => {
|
|
549
|
-
var _a, _b;
|
|
550
|
-
return [(_b = (_a = pod.status) == null ? void 0 : _a.message) != null ? _b : ""];
|
|
551
|
-
}
|
|
552
|
-
},
|
|
553
|
-
{
|
|
554
|
-
severity: 4,
|
|
555
|
-
errorExplanation: "containers-restarting",
|
|
556
|
-
errorExists: (pod) => {
|
|
557
|
-
return totalRestarts(pod) > 3;
|
|
558
|
-
},
|
|
559
|
-
messageAccessor: (pod) => {
|
|
560
|
-
var _a, _b;
|
|
561
|
-
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`);
|
|
562
|
-
}
|
|
563
|
-
},
|
|
564
|
-
{
|
|
565
|
-
severity: 5,
|
|
566
|
-
errorExplanation: "condition-message-present",
|
|
567
|
-
errorExists: (pod) => {
|
|
568
|
-
var _a, _b;
|
|
569
|
-
return ((_b = (_a = pod.status) == null ? void 0 : _a.conditions) != null ? _b : []).some((c) => c.message !== void 0);
|
|
570
|
-
},
|
|
571
|
-
messageAccessor: (pod) => {
|
|
572
|
-
var _a, _b;
|
|
573
|
-
return ((_b = (_a = pod.status) == null ? void 0 : _a.conditions) != null ? _b : []).filter((c) => c.message !== void 0).map((c) => {
|
|
574
|
-
var _a2;
|
|
575
|
-
return (_a2 = c.message) != null ? _a2 : "";
|
|
576
|
-
});
|
|
577
|
-
}
|
|
578
|
-
},
|
|
579
|
-
{
|
|
580
|
-
severity: 6,
|
|
581
|
-
errorExplanation: "container-waiting",
|
|
582
|
-
errorExists: (pod) => {
|
|
583
|
-
var _a, _b;
|
|
584
|
-
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).some(
|
|
585
|
-
(cs) => {
|
|
586
|
-
var _a2, _b2;
|
|
587
|
-
return ((_b2 = (_a2 = cs.state) == null ? void 0 : _a2.waiting) == null ? void 0 : _b2.message) !== void 0;
|
|
588
|
-
}
|
|
589
|
-
);
|
|
590
|
-
},
|
|
591
|
-
messageAccessor: (pod) => {
|
|
592
|
-
var _a, _b;
|
|
593
|
-
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).filter((cs) => {
|
|
594
|
-
var _a2, _b2;
|
|
595
|
-
return ((_b2 = (_a2 = cs.state) == null ? void 0 : _a2.waiting) == null ? void 0 : _b2.message) !== void 0;
|
|
596
|
-
}).map((cs) => {
|
|
597
|
-
var _a2, _b2, _c;
|
|
598
|
-
return (_c = (_b2 = (_a2 = cs.state) == null ? void 0 : _a2.waiting) == null ? void 0 : _b2.message) != null ? _c : "";
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
},
|
|
602
|
-
{
|
|
603
|
-
severity: 4,
|
|
604
|
-
errorExplanation: "container-last-state-error",
|
|
605
|
-
errorExists: (pod) => {
|
|
606
|
-
var _a, _b;
|
|
607
|
-
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).some(
|
|
608
|
-
(cs) => {
|
|
609
|
-
var _a2, _b2, _c;
|
|
610
|
-
return ((_c = (_b2 = (_a2 = cs.lastState) == null ? void 0 : _a2.terminated) == null ? void 0 : _b2.reason) != null ? _c : "") === "Error";
|
|
611
|
-
}
|
|
612
|
-
);
|
|
613
|
-
},
|
|
614
|
-
messageAccessor: (pod) => {
|
|
615
|
-
var _a, _b;
|
|
616
|
-
return ((_b = (_a = pod.status) == null ? void 0 : _a.containerStatuses) != null ? _b : []).filter((cs) => {
|
|
617
|
-
var _a2, _b2, _c;
|
|
618
|
-
return ((_c = (_b2 = (_a2 = cs.lastState) == null ? void 0 : _a2.terminated) == null ? void 0 : _b2.reason) != null ? _c : "") === "Error";
|
|
619
|
-
}).map(
|
|
620
|
-
(cs) => {
|
|
621
|
-
var _a2, _b2;
|
|
622
|
-
return `container=${cs.name} exited with error code (${(_b2 = (_a2 = cs.lastState) == null ? void 0 : _a2.terminated) == null ? void 0 : _b2.exitCode})`;
|
|
623
|
-
}
|
|
624
|
-
);
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
];
|
|
628
|
-
const detectErrorsInPods = (pods, clusterName) => detectErrorsInObjects(pods, "Pod", clusterName, podErrorMappers);
|
|
629
|
-
|
|
630
|
-
const deploymentErrorMappers = [
|
|
631
|
-
{
|
|
632
|
-
// this is probably important
|
|
633
|
-
severity: 6,
|
|
634
|
-
errorExplanation: "condition-message-present",
|
|
635
|
-
errorExists: (deployment) => {
|
|
636
|
-
var _a, _b;
|
|
637
|
-
return ((_b = (_a = deployment.status) == null ? void 0 : _a.conditions) != null ? _b : []).filter((c) => c.status === "False").some((c) => c.message !== void 0);
|
|
638
|
-
},
|
|
639
|
-
messageAccessor: (deployment) => {
|
|
640
|
-
var _a, _b;
|
|
641
|
-
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) => {
|
|
642
|
-
var _a2;
|
|
643
|
-
return (_a2 = c.message) != null ? _a2 : "";
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
];
|
|
648
|
-
const detectErrorsInDeployments = (deployments, clusterName) => detectErrorsInObjects(
|
|
649
|
-
deployments,
|
|
650
|
-
"Deployment",
|
|
651
|
-
clusterName,
|
|
652
|
-
deploymentErrorMappers
|
|
653
|
-
);
|
|
654
|
-
|
|
655
|
-
const hpaErrorMappers = [
|
|
656
|
-
{
|
|
657
|
-
// this is probably important
|
|
658
|
-
severity: 8,
|
|
659
|
-
errorExplanation: "hpa-max-current-replicas",
|
|
660
|
-
errorExists: (hpa) => {
|
|
661
|
-
var _a, _b, _c;
|
|
662
|
-
return ((_b = (_a = hpa.spec) == null ? void 0 : _a.maxReplicas) != null ? _b : -1) === ((_c = hpa.status) == null ? void 0 : _c.currentReplicas);
|
|
663
|
-
},
|
|
664
|
-
messageAccessor: (hpa) => {
|
|
665
|
-
var _a, _b, _c;
|
|
666
|
-
return [
|
|
667
|
-
`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})`
|
|
668
|
-
];
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
];
|
|
672
|
-
const detectErrorsInHpa = (hpas, clusterName) => detectErrorsInObjects(
|
|
673
|
-
hpas,
|
|
674
|
-
"HorizontalPodAutoscaler",
|
|
675
|
-
clusterName,
|
|
676
|
-
hpaErrorMappers
|
|
677
|
-
);
|
|
678
|
-
|
|
679
|
-
const detectErrors = (objects) => {
|
|
680
|
-
const errors = /* @__PURE__ */ new Map();
|
|
681
|
-
for (const clusterResponse of objects.items) {
|
|
682
|
-
let clusterErrors = [];
|
|
683
|
-
const groupedResponses = groupResponses(clusterResponse.resources);
|
|
684
|
-
clusterErrors = clusterErrors.concat(
|
|
685
|
-
detectErrorsInPods(groupedResponses.pods, clusterResponse.cluster.name)
|
|
686
|
-
);
|
|
687
|
-
clusterErrors = clusterErrors.concat(
|
|
688
|
-
detectErrorsInDeployments(
|
|
689
|
-
groupedResponses.deployments,
|
|
690
|
-
clusterResponse.cluster.name
|
|
691
|
-
)
|
|
692
|
-
);
|
|
693
|
-
clusterErrors = clusterErrors.concat(
|
|
694
|
-
detectErrorsInHpa(
|
|
695
|
-
groupedResponses.horizontalPodAutoscalers,
|
|
696
|
-
clusterResponse.cluster.name
|
|
697
|
-
)
|
|
698
|
-
);
|
|
699
|
-
errors.set(clusterResponse.cluster.name, clusterErrors);
|
|
700
|
-
}
|
|
701
|
-
return errors;
|
|
702
|
-
};
|
|
703
|
-
|
|
704
752
|
const generateAuth = async (entity, kubernetesApi, kubernetesAuthProvidersApi) => {
|
|
705
753
|
var _a;
|
|
706
754
|
const clusters = await kubernetesApi.getClusters();
|
|
@@ -2476,9 +2524,9 @@ const KubernetesContent = ({
|
|
|
2476
2524
|
}
|
|
2477
2525
|
))
|
|
2478
2526
|
), (kubernetesObjects == null ? void 0 : kubernetesObjects.items.length) > 0 && (kubernetesObjects == null ? void 0 : kubernetesObjects.items.map((item, i) => {
|
|
2479
|
-
var _a2
|
|
2527
|
+
var _a2;
|
|
2480
2528
|
const podsWithErrors = new Set(
|
|
2481
|
-
(
|
|
2529
|
+
(_a2 = detectedErrors.get(item.cluster.name)) == null ? void 0 : _a2.filter((de) => de.sourceRef.kind === "Pod").map((de) => de.sourceRef.name)
|
|
2482
2530
|
);
|
|
2483
2531
|
return /* @__PURE__ */ React__default.createElement(Grid, { item: true, key: i, xs: 12 }, /* @__PURE__ */ React__default.createElement(
|
|
2484
2532
|
Cluster,
|