Skip to content

Commit

Permalink
Show all resources for selected operator in the topology side panel
Browse files Browse the repository at this point in the history
  • Loading branch information
jeff-phillips-18 committed Sep 18, 2020
1 parent 22306a9 commit 49cb122
Show file tree
Hide file tree
Showing 8 changed files with 312 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,29 @@ import { K8sResourceKind, referenceFor, modelFor } from '@console/internal/modul
type TopologyGroupResourceItemProps = {
item: K8sResourceKind;
releaseNamespace: string;
linkForResource?: (obj: K8sResourceKind) => React.ReactElement;
};

const TopologyGroupResourceItem: React.FC<TopologyGroupResourceItemProps> = ({
item,
releaseNamespace,
linkForResource,
}) => {
const {
metadata: { name, namespace },
} = item;
const kind = referenceFor(item);
const model = modelFor(kind);
const resourceNamespace = model.namespaced ? namespace || releaseNamespace : null;

const link = linkForResource ? (
linkForResource(item)
) : (
<ResourceLink kind={kind} name={name} namespace={resourceNamespace} />
);
return (
<li className="list-group-item container-fluid">
<div className="row">
<span className="col-xs-12">
<ResourceLink kind={kind} name={name} namespace={resourceNamespace} />
</span>
<span className="col-xs-12">{link}</span>
</div>
</li>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import TopologyGroupResourceItem from './TopologyGroupResourceItem';
type TopologyGroupResourceListProps = {
resources: K8sResourceKind[];
releaseNamespace: string;
linkForResource?: (obj: K8sResourceKind) => React.ReactElement;
};

const TopologyGroupResourceList: React.FC<TopologyGroupResourceListProps> = ({
resources,
releaseNamespace,
linkForResource,
}) => {
return (
<ul className="list-group">
Expand All @@ -20,6 +22,7 @@ const TopologyGroupResourceList: React.FC<TopologyGroupResourceListProps> = ({
key={resource.metadata.name}
item={resource}
releaseNamespace={releaseNamespace}
linkForResource={linkForResource}
/>
))}
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import TopologyGroupResourceList from './TopologyGroupResourceList';
type TopologyGroupResourcesPanelProps = {
manifestResources: K8sResourceKind[];
releaseNamespace: string;
linkForResource?: (obj: K8sResourceKind) => React.ReactElement;
};

const TopologyGroupResourcesPanel: React.SFC<TopologyGroupResourcesPanelProps> = ({
manifestResources,
releaseNamespace,
linkForResource,
}) => {
const kinds = manifestResources
.reduce((resourceKinds, resource) => {
Expand All @@ -29,7 +31,11 @@ const TopologyGroupResourcesPanel: React.SFC<TopologyGroupResourcesPanelProps> =
lists.push(
<div key={model.kind}>
<SidebarSectionHeading text={model.labelPlural} />
<TopologyGroupResourceList resources={resources} releaseNamespace={releaseNamespace} />
<TopologyGroupResourceList
resources={resources}
releaseNamespace={releaseNamespace}
linkForResource={linkForResource}
/>
</div>,
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,84 @@
import * as React from 'react';
import { SimpleTabNav, ResourceSummary, SectionHeading } from '@console/internal/components/utils';
import { connect } from 'react-redux';
import * as UIActions from '@console/internal/actions/ui';
import { ManagedByOperatorResourceLink } from '@console/internal/components/utils/managed-by';
import { modelFor, referenceFor, referenceForModel } from '@console/internal/module/k8s';
import {
SimpleTabNav,
ResourceSummary,
SectionHeading,
ActionsMenu,
StatusBox,
} from '@console/internal/components/utils';
import TopologyOperatorBackedResources from './TopologyOperatorBackedResources';
import { TopologyDataObject } from '../topology-types';
import { OperatorGroupData } from './operator-topology-types';
import { ManagedByOperatorResourceLink } from '@console/internal/components/utils/managed-by';
import {
ClusterServiceVersionAction,
isClusterServiceVersionAction,
useExtensions,
} from '@console/plugin-sdk/src';
import {
ClusterServiceVersionKind,
ClusterServiceVersionModel,
} from '@console/operator-lifecycle-manager/src';
import { useK8sWatchResources } from '@console/internal/components/utils/k8s-watch-hook';
import { getOperandActions } from '@console/operator-lifecycle-manager/src/components/operand';

type PropsFromState = {
selectedDetailsTab?: any;
};

type PropsFromDispatch = {
onClickTab?: (name: string) => void;
};

const stateToProps = ({ UI }): PropsFromState => ({
selectedDetailsTab: UI.getIn(['overview', 'selectedDetailsTab']),
});

const dispatchToProps = (dispatch): PropsFromDispatch => ({
onClickTab: (name) => dispatch(UIActions.selectOverviewDetailsTab(name)),
});

export type TopologyOperatorBackedPanelProps = {
item: TopologyDataObject<OperatorGroupData>;
};

const TopologyOperatorBackedPanel: React.FC<TopologyOperatorBackedPanelProps> = ({ item }) => {
const ConnectedTopologyOperatorBackedPanel: React.FC<PropsFromState &
PropsFromDispatch &
TopologyOperatorBackedPanelProps> = ({ item, onClickTab, selectedDetailsTab }) => {
const { name, resource } = item;
const { namespace } = resource.metadata;
const csvName = resource.metadata.selfLink.split('/').pop();
const ResourcesSection = () => <TopologyOperatorBackedResources item={item} />;
const reference = referenceFor(resource);
const actionExtensions = useExtensions<ClusterServiceVersionAction>(
isClusterServiceVersionAction,
);
const menuActions = React.useMemo(() => getOperandActions(reference, actionExtensions, csvName), [
reference,
actionExtensions,
csvName,
]);
const actions = menuActions.map((a) => a(modelFor(reference), resource));
const resourcesList = React.useMemo(() => {
return {
csv: {
kind: referenceForModel(ClusterServiceVersionModel),
name: csvName,
namespace,
isList: false,
},
};
}, [csvName, namespace]);

const resources = useK8sWatchResources(resourcesList);
const ResourcesSection = () => (
<TopologyOperatorBackedResources
item={item}
csv={resources.csv.data as ClusterServiceVersionKind}
/>
);
const DetailsSection = () => (
<div className="overview__sidebar-pane-body">
<SectionHeading text="Operator Details" />
Expand All @@ -36,18 +102,39 @@ const TopologyOperatorBackedPanel: React.FC<TopologyOperatorBackedPanelProps> =
}}
/>
</div>
<div className="co-actions">
<ActionsMenu actions={actions} />
</div>
</h1>
</div>
<SimpleTabNav
tabs={[
{ name: 'Details', component: DetailsSection },
{ name: 'Resources', component: ResourcesSection },
]}
tabProps={null}
additionalClassNames="co-m-horizontal-nav__menu--within-sidebar co-m-horizontal-nav__menu--within-overview-sidebar"
/>
<StatusBox
data={resources.csv.data}
loaded={resources.csv.loaded}
loadError={resources.csv.loadError}
label="Operator Details"
>
<SimpleTabNav
selectedTab={selectedDetailsTab || 'Resources'}
onClickTab={onClickTab}
tabs={[
{ name: 'Details', component: DetailsSection },
{ name: 'Resources', component: ResourcesSection },
]}
tabProps={null}
additionalClassNames="co-m-horizontal-nav__menu--within-sidebar co-m-horizontal-nav__menu--within-overview-sidebar"
/>
</StatusBox>
</div>
);
};

const TopologyOperatorBackedPanel = connect<
PropsFromState,
PropsFromDispatch,
TopologyOperatorBackedPanelProps
>(
stateToProps,
dispatchToProps,
)(ConnectedTopologyOperatorBackedPanel);

export default TopologyOperatorBackedPanel;
Original file line number Diff line number Diff line change
@@ -1,22 +1,153 @@
import * as React from 'react';
import { Link } from 'react-router-dom';
import {
GroupVersionKind,
K8sResourceKind,
modelFor,
referenceFor,
referenceForGroupVersionKind,
referenceForModel,
} from '@console/internal/module/k8s';
import {
ClusterServiceVersionKind,
ClusterServiceVersionModel,
CRDDescription,
providedAPIsFor,
referenceForProvidedAPI,
} from '@console/operator-lifecycle-manager/src';
import {
flattenCsvResources,
linkForCsvResource,
} from '@console/operator-lifecycle-manager/src/components/k8s-resource';
import { Firehose, ResourceIcon, StatusBox } from '@console/internal/components/utils';
import { TopologyDataObject } from '../topology-types';
import TopologyGroupResourcesPanel from '../components/TopologyGroupResourcesPanel';

type OperatorResourcesProps = {
namespace: string;
resources?: {
[kind: string]: { data: K8sResourceKind[] };
};
loaded?: boolean;
loadError?: string;
flatten: (resources: { [kind: string]: { data: K8sResourceKind[] } }) => K8sResourceKind[];
linkForResource?: (obj: K8sResourceKind) => React.ReactElement;
};

const OperatorResources: React.FC<OperatorResourcesProps> = ({
namespace,
resources,
loaded,
loadError,
flatten,
linkForResource,
}) => {
const manifestResources = flatten(resources);
return (
<StatusBox data={manifestResources} loaded={loaded} loadError={loadError} label="Resources">
<TopologyGroupResourcesPanel
manifestResources={flatten(resources)}
releaseNamespace={namespace}
linkForResource={linkForResource}
/>
</StatusBox>
);
};

type OperatorResourcesGetterProps = {
modelReference: GroupVersionKind;
csv: ClusterServiceVersionKind;
namespace: string;
flatten: (resources: { [kind: string]: { data: K8sResourceKind[] } }) => K8sResourceKind[];
};

const OperatorResourcesGetter: React.FC<OperatorResourcesGetterProps> = ({
csv,
modelReference,
namespace,
flatten,
}) => {
const providedAPI = providedAPIsFor(csv).find(
(desc) => referenceForProvidedAPI(desc) === modelReference,
);
const linkForResource = (obj: K8sResourceKind) => {
return linkForCsvResource(obj, providedAPI, csv.metadata.name);
};
const defaultResources = ['Deployment', 'Service', 'ReplicaSet', 'Pod', 'Secret', 'ConfigMap'];
const resourcesToGet =
providedAPI?.resources ??
(defaultResources.map((kind) => ({
kind,
})) as CRDDescription['resources']);

const firehoseResources = resourcesToGet.reduce((acc, descriptor) => {
const { name, kind, version } = descriptor;
const group = name ? name.substring(name.indexOf('.') + 1) : '';
const reference = group ? referenceForGroupVersionKind(group)(version)(kind) : kind;
const model = modelFor(reference);
acc.push({
prop: kind,
kind: model && !model.crd ? kind : reference,
namespaced: model ? model.namespaced : true,
namespace,
isList: true,
optional: true,
});
return acc;
}, []);

return (
<Firehose resources={firehoseResources}>
<OperatorResources
namespace={namespace}
flatten={flatten}
linkForResource={linkForResource}
/>
</Firehose>
);
};

type TopologyOperatorBackedResourcesProps = {
item: TopologyDataObject;
csv: ClusterServiceVersionKind;
};

const TopologyOperatorBackedResources: React.FC<TopologyOperatorBackedResourcesProps> = ({
item,
csv,
}) => {
const { groupResources = [] } = item;
const finalRes = groupResources.map((val) => val.resource).filter((r) => r !== undefined);
const { resource } = item;
const { namespace } = resource.metadata;
const reference = referenceFor(resource);
const flatten = flattenCsvResources(resource);
const getManagedByCSVResourceLink = () => {
const model = modelFor(referenceFor(csv));
const { name } = csv.metadata;
const { kind } = model;

const link = `/k8s/ns/${namespace}/${referenceForModel(ClusterServiceVersionModel)}/${name}`;

return (
<div className="co-m-pane__heading-owner">
Managed by{' '}
<span className="co-resource-item">
<ResourceIcon kind={kind} />
<Link to={link} className="co-resource-item__resource-name" data-test-operand-link={name}>
{name}
</Link>
</span>
</div>
);
};

return (
<div className="overview__sidebar-pane-body">
<TopologyGroupResourcesPanel
manifestResources={finalRes}
releaseNamespace={item.resources.obj.metadata.namespace}
{getManagedByCSVResourceLink()}
<OperatorResourcesGetter
csv={csv}
flatten={flatten}
namespace={namespace}
modelReference={reference}
/>
</div>
);
Expand Down

0 comments on commit 49cb122

Please sign in to comment.