surface cluster title in ErrorReporting table

Signed-off-by: Jamie Klassen <jamie.klassen@broadcom.com>
This commit is contained in:
Jamie Klassen
2024-01-24 10:46:42 -05:00
parent 74770c81ec
commit b01c86c2b5
5 changed files with 174 additions and 5 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-kubernetes': patch
'@backstage/plugin-kubernetes-react': patch
---
The `ErrorReporting` component's cluster column now displays cluster titles when
specified.
+2
View File
@@ -176,11 +176,13 @@ export type ErrorPanelProps = {
// @public (undocumented)
export const ErrorReporting: ({
detectedErrors,
clusters,
}: ErrorReportingProps) => React_3.JSX.Element;
// @public (undocumented)
export type ErrorReportingProps = {
detectedErrors: DetectedErrorsByCluster;
clusters: ClusterAttributes[];
};
// @public
@@ -0,0 +1,150 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { DetectedError } from '@backstage/plugin-kubernetes-common';
import { renderInTestApp } from '@backstage/test-utils';
import { MatcherFunction, screen } from '@testing-library/react';
import { ErrorReporting } from './ErrorReporting';
describe('ErrorReporting', () => {
const matchTextContent =
(text: string): MatcherFunction =>
(_, node) =>
node?.textContent?.includes(text) ?? false;
it('sorts errors by severity', async () => {
await renderInTestApp(
<ErrorReporting
detectedErrors={
new Map<string, DetectedError[]>([
[
'cluster',
[
{
type: 'readiness-probe-taking-too-long',
message:
'The container my-container failed to start properly, but is not crashing',
severity: 4,
proposedFix: undefined,
sourceRef: {
name: 'my-pod',
namespace: 'default',
kind: 'Pod',
apiGroup: 'v1',
},
occurrenceCount: 1,
},
{
type: 'condition-message-present',
message: 'some condition message',
severity: 6,
sourceRef: {
name: 'my-deployment',
namespace: 'default',
kind: 'Deployment',
apiGroup: 'apps/v1',
},
occurrenceCount: 1,
},
],
],
])
}
clusters={[{ name: 'cluster' }]}
/>,
);
expect(screen.getAllByRole('row')).toEqual([
expect.anything(),
screen.getByText(matchTextContent('some condition message'), {
selector: 'tr',
}),
screen.getByText(
matchTextContent(
'The container my-container failed to start properly, but is not crashing',
),
{ selector: 'tr' },
),
expect.anything(),
]);
});
it('displays cluster name', async () => {
await renderInTestApp(
<ErrorReporting
detectedErrors={
new Map<string, DetectedError[]>([
[
'my-cluster',
[
{
type: 'condition-message-present',
message: 'some condition message',
severity: 6,
sourceRef: {
name: 'my-deployment',
namespace: 'default',
kind: 'Deployment',
apiGroup: 'apps/v1',
},
occurrenceCount: 1,
},
],
],
])
}
clusters={[{ name: 'my-cluster' }]}
/>,
);
expect(
screen.getByRole('cell', { name: 'my-cluster' }),
).toBeInTheDocument();
});
it('displays cluster title when specified', async () => {
await renderInTestApp(
<ErrorReporting
detectedErrors={
new Map<string, DetectedError[]>([
[
'my-cluster',
[
{
type: 'condition-message-present',
message: 'some condition message',
severity: 6,
sourceRef: {
name: 'my-deployment',
namespace: 'default',
kind: 'Deployment',
apiGroup: 'apps/v1',
},
occurrenceCount: 1,
},
],
],
])
}
clusters={[{ name: 'my-cluster', title: 'cluster-title' }]}
/>,
);
expect(
screen.getByRole('cell', { name: 'cluster-title' }),
).toBeInTheDocument();
});
});
@@ -15,6 +15,7 @@
*/
import * as React from 'react';
import {
ClusterAttributes,
DetectedError,
DetectedErrorsByCluster,
} from '@backstage/plugin-kubernetes-common';
@@ -27,13 +28,14 @@ import { Table, TableColumn } from '@backstage/core-components';
*/
export type ErrorReportingProps = {
detectedErrors: DetectedErrorsByCluster;
clusters: ClusterAttributes[];
};
const columns: TableColumn<Row>[] = [
{
title: 'cluster',
width: '10%',
render: (row: Row) => row.clusterName,
render: (row: Row) => row.cluster.title || row.cluster.name,
},
{
title: 'namespace',
@@ -60,7 +62,7 @@ const columns: TableColumn<Row>[] = [
];
interface Row {
clusterName: string;
cluster: ClusterAttributes;
error: DetectedError;
}
@@ -78,11 +80,14 @@ const sortBySeverity = (a: Row, b: Row) => {
*
* @public
*/
export const ErrorReporting = ({ detectedErrors }: ErrorReportingProps) => {
export const ErrorReporting = ({
detectedErrors,
clusters,
}: ErrorReportingProps) => {
const errors = Array.from(detectedErrors.entries())
.flatMap(([clusterName, resourceErrors]) => {
return resourceErrors.map(e => ({
clusterName,
cluster: clusters.find(c => c.name === clusterName)!,
error: e,
}));
})
+6 -1
View File
@@ -50,6 +50,8 @@ export const KubernetesContent = ({
refreshIntervalMs,
);
const clusters = kubernetesObjects?.items.map(item => item.cluster) ?? [];
const clustersWithErrors =
kubernetesObjects?.items.filter(r => r.errors.length > 0) ?? [];
@@ -93,7 +95,10 @@ export const KubernetesContent = ({
{kubernetesObjects && (
<Grid container spacing={3} direction="column">
<Grid item>
<ErrorReporting detectedErrors={detectedErrors} />
<ErrorReporting
detectedErrors={detectedErrors}
clusters={clusters}
/>
</Grid>
<Grid item>
<Typography variant="h3">Your Clusters</Typography>