Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions ui/apps/platform/src/Containers/NetworkGraph/NetworkGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import CidrBlockSideBar from './cidr/CidrBlockSideBar';
import ExternalEntitiesSideBar from './external/ExternalEntitiesSideBar';

import './Topology.css';
import { getNodeById } from './utils/networkGraphUtils';

// TODO: move these type defs to a central location
export const UrlDetailType = {
Expand All @@ -38,11 +39,6 @@ export const UrlDetailType = {
export type UrlDetailTypeKey = keyof typeof UrlDetailType;
export type UrlDetailTypeValue = typeof UrlDetailType[UrlDetailTypeKey];

function findEntityById(graphModel: Model, id: string | undefined): NodeModel | undefined {
const entity = graphModel.nodes?.find((node: { id: string }) => node.id === id);
return entity;
}

function getUrlParamsForEntity(selectedEntity: NodeModel): [UrlDetailTypeValue, string] {
const detailType = UrlDetailType[selectedEntity.data.type];
const detailId = selectedEntity.id;
Expand Down Expand Up @@ -95,7 +91,7 @@ function setEdges(controller, detailId) {
const TopologyComponent = ({ model }: TopologyComponentProps) => {
const history = useHistory();
const { detailId } = useParams();
const selectedEntity = detailId && findEntityById(model, detailId);
const selectedEntity = detailId && getNodeById(model, detailId);
const controller = useVisualizationController();

// to prevent error where graph hasn't initialized yet
Expand All @@ -109,7 +105,7 @@ const TopologyComponent = ({ model }: TopologyComponentProps) => {

function onSelect(ids: string[]) {
const newSelectedId = ids?.[0] || '';
const newSelectedEntity = findEntityById(model, newSelectedId);
const newSelectedEntity = getNodeById(model, newSelectedId);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (newSelectedEntity) {
Expand All @@ -132,6 +128,7 @@ const TopologyComponent = ({ model }: TopologyComponentProps) => {
return () => {
controller.removeEventListener(SELECTION_EVENT, onSelect);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [controller, model]);

const selectedIds = selectedEntity ? [selectedEntity.id] : [];
Expand All @@ -144,7 +141,11 @@ const TopologyComponent = ({ model }: TopologyComponentProps) => {
<NamespaceSideBar />
)}
{selectedEntity && selectedEntity?.data?.type === 'DEPLOYMENT' && (
<DeploymentSideBar />
<DeploymentSideBar
deploymentId={selectedEntity.id}
nodes={model?.nodes || []}
edges={model?.edges || []}
/>
)}
{selectedEntity && selectedEntity?.data?.type === 'CIDR_BLOCK' && (
<CidrBlockSideBar />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { fetchClustersAsArray, Cluster } from 'services/ClustersService';

import PageTitle from 'Components/PageTitle';
import NetworkGraph from './NetworkGraph';
import { transformData, graphModel } from './utils';
import { transformData, graphModel } from './utils/modelUtils';

import './NetworkGraphPage.css';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ import {
TextVariants,
} from '@patternfly/react-core';
import { ExclamationCircleIcon } from '@patternfly/react-icons';
import pluralize from 'pluralize';

import { Deployment } from 'types/deployment.proto';
import { ListenPort } from 'types/networkFlow.proto';
import { getDateTime } from 'utils/dateUtils';

type DeploymentDetailsProps = {
deployment: Deployment;
numExternalFlows: number;
numInternalFlows: number;
listenPorts: ListenPort[];
};

function DetailSection({ title, children }) {
const [isExpanded, setIsExpanded] = useState(true);
Expand All @@ -43,7 +55,12 @@ function DetailSection({ title, children }) {
);
}

function DeploymentDetails() {
function DeploymentDetails({
deployment,
numExternalFlows,
numInternalFlows,
listenPorts,
}: DeploymentDetailsProps) {
return (
<div className="pf-u-h-100 pf-u-p-md">
<ul>
Expand All @@ -63,7 +80,8 @@ function DeploymentDetails() {
color="red"
icon={<ExclamationCircleIcon />}
>
2 external flows
{numExternalFlows} external{' '}
{pluralize('flow', numExternalFlows)}
</Label>
</FlexItem>
<FlexItem>
Expand All @@ -72,7 +90,8 @@ function DeploymentDetails() {
color="gold"
icon={<ExclamationCircleIcon />}
>
3 internal flows
{numInternalFlows} internal{' '}
{pluralize('flow', numInternalFlows)}
</Label>
</FlexItem>
</Flex>
Expand Down Expand Up @@ -110,7 +129,19 @@ function DeploymentDetails() {
alignItems={{ default: 'alignItemsCenter' }}
>
<FlexItem>
<Label variant="outline">TCP: 2020, 2021</Label>
<LabelGroup>
{listenPorts.map(({ port, l4protocol }) => {
const protocol = l4protocol.replace(
'L4_PROTOCOL_',
''
);
return (
<Label variant="outline">
{protocol}: {port}
</Label>
);
})}
</LabelGroup>
</FlexItem>
</Flex>
</DescriptionListDescription>
Expand All @@ -128,45 +159,45 @@ function DeploymentDetails() {
<DescriptionListTerm>Name</DescriptionListTerm>
<DescriptionListDescription>
<Button variant="link" isInline>
visa-processor
{deployment.name}
</Button>
</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>Created</DescriptionListTerm>
<DescriptionListDescription>
12/09/21 | 6:03:23 PM
{getDateTime(deployment.created)}
</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>Cluster</DescriptionListTerm>
<DescriptionListDescription>
<Button variant="link" isInline>
Production
{deployment.clusterName}
</Button>
</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>Namespace</DescriptionListTerm>
<DescriptionListDescription>
<Button variant="link" isInline>
Naples
{deployment.namespace}
</Button>
</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>Replicas</DescriptionListTerm>
<DescriptionListDescription>
<Button variant="link" isInline>
2 pods
{deployment.replicas}
</Button>
</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>Service account</DescriptionListTerm>
<DescriptionListDescription>
<Button variant="link" isInline>
visa-processor
{deployment.serviceAccount}
</Button>
</DescriptionListDescription>
</DescriptionListGroup>
Expand All @@ -178,44 +209,39 @@ function DeploymentDetails() {
<DescriptionListTerm>Labels</DescriptionListTerm>
<DescriptionListDescription>
<LabelGroup>
<Label color="blue">app:visa-processor</Label>
<Label color="blue">
helm.sh/release-namespace:naples
</Label>
{Object.keys(deployment.labels).map((labelKey) => {
const labelValue = deployment.labels[labelKey];
const label = `${labelKey}:${labelValue}`;
return (
<Label key={label} color="blue">
{label}
</Label>
);
})}
</LabelGroup>
</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>Annotations</DescriptionListTerm>
<DescriptionListDescription>
<LabelGroup>
<Label color="blue">
deprecated.daemonset.template.generation:15
</Label>
<Label color="blue">
email:support@stackrox.com
</Label>
{Object.keys(deployment.annotations).map(
(annotationKey) => {
const annotationValue =
deployment.annotations[annotationKey];
const annotation = `${annotationKey}:${annotationValue}`;
return (
<Label key={annotationKey} color="blue">
{annotation}
</Label>
);
}
)}
</LabelGroup>
</DescriptionListDescription>
</DescriptionListGroup>
</DescriptionList>
</StackItem>
<StackItem>
<DescriptionList columnModifier={{ default: '2Col' }}>
<DescriptionListGroup>
<DescriptionListTerm>AddCapabilities</DescriptionListTerm>
<DescriptionListDescription>
SYS_ADMIN
</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>Privileged</DescriptionListTerm>
<DescriptionListDescription>
true
</DescriptionListDescription>
</DescriptionListGroup>
</DescriptionList>
</StackItem>
</Stack>
</DetailSection>
</li>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import React from 'react';
import {
Alert,
AlertVariant,
Badge,
Bullseye,
Flex,
FlexItem,
Spinner,
Stack,
StackItem,
Tab,
Expand All @@ -13,17 +17,52 @@ import {
TextContent,
TextVariants,
} from '@patternfly/react-core';
import { EdgeModel, NodeModel } from '@patternfly/react-topology';

import useTabs from 'hooks/patternfly/useTabs';
import useFetchDeployment from 'hooks/useFetchDeployment';
import {
getListenPorts,
getNumExternalFlows,
getNumInternalFlows,
} from '../utils/networkGraphUtils';

import DeploymentDetails from './DeploymentDetails';
import DeploymentNetworkPolicies from './DeploymentNetworkPolicies';
import DeploymentFlows from './DeploymentFlows';

function DeploymentSideBar() {
type DeploymentSideBarProps = {
deploymentId: string;
nodes: NodeModel[];
edges: EdgeModel[];
};

function DeploymentSideBar({ deploymentId, nodes, edges }: DeploymentSideBarProps) {
// component state
const { deployment, isLoading, error } = useFetchDeployment(deploymentId);
const { activeKeyTab, onSelectTab } = useTabs({
defaultTab: 'Details',
});

// derived values
const numExternalFlows = getNumExternalFlows(nodes, edges, deploymentId);
const numInternalFlows = getNumInternalFlows(nodes, edges, deploymentId);
const listenPorts = getListenPorts(nodes, deploymentId);

if (isLoading) {
return (
<Bullseye>
<Spinner isSVG size="lg" />
</Bullseye>
);
}

if (error) {
return (
<Alert isInline variant={AlertVariant.danger} title={error} className="pf-u-mb-lg" />
);
}

return (
<Stack>
<StackItem>
Expand All @@ -34,15 +73,15 @@ function DeploymentSideBar() {
<FlexItem>
<TextContent>
<Text component={TextVariants.h1} className="pf-u-font-size-xl">
visa-processor
{deployment?.name}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we have this all the time via the model?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deployment value actually comes from the API to get the deployment details so that's why it could be undefined until the API returns a response. You're right though, we could find the deployment name through the selected node in the model, but I thought it made sense to use the deployment value. We can get around this by having a loading state and empty state

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh i see. yeah i think it makes sense to use both the graph deployment data and the deployment API response since we're using both for the side panel (at least for the flows tab) anyways

</Text>
</TextContent>
<TextContent>
<Text
component={TextVariants.h2}
className="pf-u-font-size-sm pf-u-color-200"
>
in &quot;production / naples&quot;
in &quot;{deployment?.clusterName} / {deployment?.namespace}&quot;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here as above

</Text>
</TextContent>
</FlexItem>
Expand Down Expand Up @@ -74,7 +113,14 @@ function DeploymentSideBar() {
</StackItem>
<StackItem isFilled style={{ overflow: 'auto' }}>
<TabContent eventKey="Details" id="Details" hidden={activeKeyTab !== 'Details'}>
<DeploymentDetails />
{deployment && (
<DeploymentDetails
deployment={deployment}
numExternalFlows={numExternalFlows}
numInternalFlows={numInternalFlows}
listenPorts={listenPorts}
/>
)}
</TabContent>
<TabContent eventKey="Flows" id="Flows" hidden={activeKeyTab !== 'Flows'}>
<DeploymentFlows />
Expand Down
Loading