feat: more work on the edges table
Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
parent
807125d8cc
commit
7490c55a1d
|
|
@ -24,12 +24,29 @@ export interface Metric {
|
|||
};
|
||||
}
|
||||
|
||||
export interface Edge {
|
||||
src: {
|
||||
namespace: string;
|
||||
name: string;
|
||||
type: MetricType;
|
||||
};
|
||||
dst: {
|
||||
namespace: string;
|
||||
name: string;
|
||||
type: MetricType;
|
||||
};
|
||||
clientId: string;
|
||||
serverId: string;
|
||||
noIdentityMsg: string;
|
||||
}
|
||||
|
||||
type MetricType = Partial<'deployment' | 'service' | 'authority' | 'pod'>;
|
||||
|
||||
export interface DeploymentResponse {
|
||||
incoming: Metric[];
|
||||
outgoing: Metric[];
|
||||
current: Metric;
|
||||
edges: Edge[];
|
||||
}
|
||||
|
||||
export interface L5dClient {
|
||||
|
|
|
|||
|
|
@ -32,12 +32,9 @@ const useStyles = makeStyles({
|
|||
export const DependenciesCard = () => {
|
||||
const styles = useStyles();
|
||||
const { entity } = useEntity();
|
||||
const { stats, loading } = useStatsForEntity(entity);
|
||||
const content = () => {
|
||||
if (loading && !stats) {
|
||||
return <Typography paragraph>Loading...</Typography>;
|
||||
}
|
||||
const { stats } = useStatsForEntity(entity);
|
||||
|
||||
const content = () => {
|
||||
if (!stats?.incoming.length && !stats?.outgoing.length) {
|
||||
return (
|
||||
<Typography paragraph>
|
||||
|
|
@ -46,6 +43,7 @@ export const DependenciesCard = () => {
|
|||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
return <OctopusGraph stats={stats} entity={entity} />;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1,161 @@
|
|||
export const EdgesTable = () => {};
|
||||
import React from 'react';
|
||||
import { EntityRefLink, useEntity } from '@backstage/plugin-catalog-react';
|
||||
import { useStatsForEntity } from '../hooks/useStatsForEntity';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
Divider,
|
||||
Chip,
|
||||
makeStyles,
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
import CheckCircle from '@material-ui/icons/CheckCircle';
|
||||
import RemoveCircle from '@material-ui/icons/RemoveCircle';
|
||||
|
||||
import { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
gridItemCard: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: 'calc(100% - 10px)', // for pages without content header
|
||||
marginBottom: '10px',
|
||||
},
|
||||
|
||||
gridItemCardContent: {
|
||||
flex: 1,
|
||||
},
|
||||
header: {
|
||||
padding: 16,
|
||||
},
|
||||
});
|
||||
|
||||
export const EdgesTable = () => {
|
||||
const { entity } = useEntity();
|
||||
const styles = useStyles();
|
||||
const { stats } = useStatsForEntity(entity);
|
||||
|
||||
if (!stats) {
|
||||
return (
|
||||
<Card className={styles.gridItemCard}>
|
||||
<CardHeader title="Mesh Edges" className={styles.header} />
|
||||
<Divider />
|
||||
<CardContent className={styles.gridItemCardContent}>
|
||||
<Typography paragraph>
|
||||
This service doesn't look like it's tagged with the right service,
|
||||
or linkerd is not injected.
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
const incoming =
|
||||
stats?.incoming?.map(edge => {
|
||||
const matchedEdge = stats?.edges.find(
|
||||
e =>
|
||||
e.dst.namespace === stats.current.namespace &&
|
||||
e.dst.name === stats.current.name &&
|
||||
e.dst.type === stats.current.type &&
|
||||
e.src.type === edge.type &&
|
||||
e.src.name === edge.name &&
|
||||
e.src.namespace === edge.namespace,
|
||||
);
|
||||
|
||||
return {
|
||||
...edge,
|
||||
direction: 'incoming',
|
||||
secure: matchedEdge?.noIdentityMsg === '',
|
||||
};
|
||||
}) ?? [];
|
||||
|
||||
const outgoing =
|
||||
stats?.outgoing?.map(edge => {
|
||||
const matchedEdge = stats?.edges.find(
|
||||
e =>
|
||||
e.src.namespace === stats.current.namespace &&
|
||||
e.src.name === stats.current.name &&
|
||||
e.src.type === stats.current.type &&
|
||||
e.dst.type === edge.type &&
|
||||
e.dst.name === edge.name &&
|
||||
e.dst.namespace === edge.namespace,
|
||||
);
|
||||
|
||||
return {
|
||||
...edge,
|
||||
direction: 'outgoing',
|
||||
secure: matchedEdge?.noIdentityMsg === '',
|
||||
};
|
||||
}) ?? [];
|
||||
|
||||
const edges = [...incoming, ...outgoing]
|
||||
.filter(f => f.type === 'deployment')
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
return (
|
||||
<Card className={styles.gridItemCard}>
|
||||
<CardHeader title="Mesh Edges" className={styles.header} />
|
||||
<Divider />
|
||||
<CardContent className={styles.gridItemCardContent}>
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Direction</TableCell>
|
||||
<TableCell align="right">Name</TableCell>
|
||||
<TableCell align="right">RPS</TableCell>
|
||||
<TableCell align="right">S/R</TableCell>
|
||||
<TableCell align="right">TLS</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{edges.map(row => (
|
||||
<TableRow key={row.name}>
|
||||
<TableCell component="th" scope="row">
|
||||
<Chip
|
||||
label={
|
||||
row.direction === 'incoming' ? 'Incoming' : 'Outgoing'
|
||||
}
|
||||
color={
|
||||
row.direction === 'incoming' ? 'primary' : 'secondary'
|
||||
}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<EntityRefLink
|
||||
entityRef={stringifyEntityRef({
|
||||
kind: 'component',
|
||||
name: row.name,
|
||||
namespace: row.namespace,
|
||||
})}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell align="right">{`${row.requestRate.toFixed(
|
||||
2,
|
||||
)}`}</TableCell>
|
||||
<TableCell align="right">
|
||||
{`${Math.round(row.successRate * 100)}%`}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{row.secure ? (
|
||||
<CheckCircle color="primary" />
|
||||
) : (
|
||||
<RemoveCircle color="secondary" />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,13 +9,19 @@ import { useStatsForEntity } from '../hooks/useStatsForEntity';
|
|||
|
||||
export const IsMeshedBanner = () => {
|
||||
const { entity } = useEntity();
|
||||
const { stats, loading } = useStatsForEntity(entity);
|
||||
const { stats } = useStatsForEntity(entity);
|
||||
|
||||
if (loading || !stats) {
|
||||
return null;
|
||||
if (!stats) {
|
||||
return (
|
||||
<Grid item xs={12}>
|
||||
<Alert icon={<ErrorIcon fontSize="inherit" />} severity="error">
|
||||
Looks like this component is not currently meshed with Linkerd
|
||||
</Alert>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
if (stats.current) {
|
||||
if (stats?.current) {
|
||||
if (stats.current.pods.meshedPodsPercentage === 0) {
|
||||
return (
|
||||
<Grid item xs={12}>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { DeploymentResponse } from '../../api/types';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import 'reactflow/dist/style.css';
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import ReactFlow, {
|
||||
useNodesState,
|
||||
useEdgesState,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export const useStatsForEntity = (entity: Entity) => {
|
|||
const [counter, setCounter] = useState(0);
|
||||
const { value, loading } = useAsync(
|
||||
() => l5d.getStatsForEntity(entity),
|
||||
[counter],
|
||||
[counter, entity],
|
||||
);
|
||||
useInterval(() => {
|
||||
setCounter(counter + 1);
|
||||
|
|
|
|||
|
|
@ -2,4 +2,5 @@ export {
|
|||
linkerdPlugin,
|
||||
LinkerdDependenciesCard,
|
||||
LinkerdIsMeshedBanner,
|
||||
LinkerdEdgesTable,
|
||||
} from './plugin';
|
||||
|
|
|
|||
|
|
@ -60,3 +60,15 @@ export const LinkerdIsMeshedBanner = linkerdPlugin.provide(
|
|||
},
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const LinkerdEdgesTable = linkerdPlugin.provide(
|
||||
createComponentExtension({
|
||||
name: 'LinkerdEdgesTable',
|
||||
component: {
|
||||
lazy: () => import('./components/EdgesTable').then(m => m.EdgesTable),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue