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
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as _ from 'lodash-es';
import { ClusterServiceVersionsDetailsPage, ClusterServiceVersionsDetailsPageProps, ClusterServiceVersionDetails, ClusterServiceVersionDetailsProps, ClusterServiceVersionsPage, ClusterServiceVersionsPageProps, ClusterServiceVersionList, ClusterServiceVersionHeader, ClusterServiceVersionRow, ClusterServiceVersionRowProps, CRDCard, CRDCardRow } from '../../../public/components/operator-lifecycle-manager/clusterserviceversion';
import { ClusterServiceVersionKind, ClusterServiceVersionLogo, ClusterServiceVersionLogoProps, referenceForProvidedAPI, CSVConditionReason } from '../../../public/components/operator-lifecycle-manager';
import { DetailsPage, ListPage, ListHeader, ColHead, List, ListInnerProps } from '../../../public/components/factory';
import { testClusterServiceVersion, testModel } from '../../../__mocks__/k8sResourcesMocks';
import { testClusterServiceVersion } from '../../../__mocks__/k8sResourcesMocks';
import { Timestamp, MsgBox, ResourceLink, ResourceKebab, ErrorBoundary, LoadingBox, ScrollToTopOnMount, SectionHeading } from '../../../public/components/utils';
import { referenceForModel } from '../../../public/module/k8s';
import { ClusterServiceVersionModel } from '../../../public/models';
Expand Down Expand Up @@ -177,22 +177,21 @@ describe(CRDCard.displayName, () => {
const crd = testClusterServiceVersion.spec.customresourcedefinitions.owned[0];

it('renders a card with title, body, and footer', () => {
const wrapper = shallow(<CRDCard.WrappedComponent crd={crd} csv={testClusterServiceVersion} kindObj={testModel} />);
const wrapper = shallow(<CRDCard canCreate={true} crd={crd} csv={testClusterServiceVersion} />);

expect(wrapper.find('.co-crd-card__title').exists()).toBe(true);
expect(wrapper.find('.co-crd-card__body').exists()).toBe(true);
expect(wrapper.find('.co-crd-card__footer').exists()).toBe(true);
});

it('renders a link to create a new instance', () => {
const kindObj = _.cloneDeep({...testModel, verbs: ['create']});
const wrapper = shallow(<CRDCard.WrappedComponent crd={crd} csv={testClusterServiceVersion} kindObj={kindObj} />);
const wrapper = shallow(<CRDCard canCreate={true} crd={crd} csv={testClusterServiceVersion} />);

expect(wrapper.find('.co-crd-card__footer').find(Link).props().to).toEqual(`/k8s/ns/${testClusterServiceVersion.metadata.namespace}/${ClusterServiceVersionModel.plural}/${testClusterServiceVersion.metadata.name}/${referenceForProvidedAPI(crd)}/new`);
});

it('does not render link to create new instance if "create" not included in verbs for the model', () => {
const wrapper = shallow(<CRDCard.WrappedComponent crd={crd} csv={testClusterServiceVersion} kindObj={testModel} />);
it('does not render link to create new instance if `props.canCreate` is false', () => {
const wrapper = shallow(<CRDCard canCreate={false} crd={crd} csv={testClusterServiceVersion} />);

expect(wrapper.find('.co-crd-card__footer').find(Link).exists()).toBe(false);
});
Expand Down
6 changes: 6 additions & 0 deletions frontend/public/components/factory/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ const listFilters = {
}
return filters.selected.has(resource.kind);
},
'clusterserviceversion-status': (filters, csv) => {
if (!filters || !filters.selected || !filters.selected.size) {
return true;
}
return filters.selected.has(_.get(csv.status, 'reason')) || !_.includes(filters.all, _.get(csv.status, 'reason'));
},

'build-status': (phases, build) => {
if (!phases || !phases.selected || !phases.selected.size) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ import { Alert } from 'patternfly-react';
import { ProvidedAPIsPage, ProvidedAPIPage } from './clusterserviceversion-resource';
import { DetailsPage, ListHeader, ColHead, List, ListPage } from '../factory';
import { withFallback } from '../utils/error-boundary';
import { referenceForModel, referenceFor, K8sKind } from '../../module/k8s';
import { referenceForModel, referenceFor, GroupVersionKind, K8sKind } from '../../module/k8s';
import { ClusterServiceVersionModel } from '../../models';
import { FLAGS as featureFlags } from '../../features';
import { ResourceEventStream } from '../events';
import { Conditions } from '../conditions';
import { connectToModel } from '../../kinds';
import {
ClusterServiceVersionKind,
ClusterServiceVersionLogo,
Expand Down Expand Up @@ -105,6 +104,13 @@ export const ClusterServiceVersionsPage = connect(stateToProps)((props: ClusterS
Installed Operators are represented by Cluster Service Versions within this namespace. For more information, see the <ExternalLink href="https://github.com/operator-framework/operator-lifecycle-manager/blob/master/Documentation/design/architecture.md" text="Operator Lifecycle Manager documentation" />. Or create an Operator and Cluster Service Version using the <ExternalLink href="https://github.com/operator-framework/operator-sdk" text="Operator SDK" />.
</p>;

const rowFilters = [{
type: 'clusterserviceversion-status',
selected: [CSVConditionReason.CSVReasonInstallSuccessful],
reducer: (csv: ClusterServiceVersionKind) => _.get(csv.status, 'reason'),
items: [CSVConditionReason.CSVReasonInstallSuccessful, CSVConditionReason.CSVReasonCopied].map(status => ({id: status, title: status})),
}];

return <React.Fragment>
<PageHeading title="Installed Operators" />
<ListPage
Expand All @@ -114,6 +120,7 @@ export const ClusterServiceVersionsPage = connect(stateToProps)((props: ClusterS
ListComponent={ClusterServiceVersionList}
helpText={helpText}
filterLabel="Cluster Service Versions by name"
rowFilters={rowFilters}
showTitle={false} />
</React.Fragment>;
});
Expand All @@ -122,8 +129,8 @@ export const MarkdownView = (props: {content: string, outerScroll: boolean}) =>
return <AsyncComponent loader={() => import('./markdown-view').then(c => c.SyncMarkdownView)} {...props} />;
};

export const CRDCard = connectToModel((props: CRDCardProps) => {
const {csv, crd, kindObj} = props;
export const CRDCard: React.SFC<CRDCardProps> = (props) => {
const {csv, crd, canCreate} = props;
const createRoute = `/k8s/ns/${csv.metadata.namespace}/${ClusterServiceVersionModel.plural}/${csv.metadata.name}/${referenceForProvidedAPI(crd)}/new`;

return <div className="co-crd-card">
Expand All @@ -135,17 +142,27 @@ export const CRDCard = connectToModel((props: CRDCardProps) => {
<div className="co-crd-card__body" style={{margin: '0'}}>
<p>{crd.description}</p>
</div>
<div className="co-crd-card__footer">
{ (kindObj.verbs || []).some(v => v === 'create') && <Link className="co-crd-card__link" to={createRoute}>
{ canCreate && <div className="co-crd-card__footer">
<Link className="co-crd-card__link" to={createRoute}>
<span className="pficon pficon-add-circle-o" aria-hidden="true"></span> Create New
</Link> }
</div>
</Link>
</div> }
</div>;
});
};

const crdCardRowStateToProps = ({k8s}, {crdDescs}) => {
const models: K8sKind[] = crdDescs.map(desc => k8s.getIn(['RESOURCES', 'models', referenceForProvidedAPI(desc)]));
return {
crdDescs: crdDescs.filter(desc => models.find(m => referenceForModel(m) === referenceForProvidedAPI(desc))),
createable: models.filter(m => (m.verbs || []).includes('create')).map(m => referenceForModel(m)),
};
};

export const CRDCardRow: React.SFC<CRDCardRowProps> = (props) => <div className="co-crd-card-row">
{props.crdDescs.map((desc, i) => <CRDCard key={i} crd={desc} csv={props.csv} kind={referenceForProvidedAPI(desc)} />)}
</div>;
export const CRDCardRow = connect(crdCardRowStateToProps)(
(props: CRDCardRowProps) => <div className="co-crd-card-row">
{props.crdDescs.map((desc, i) => <CRDCard key={i} crd={desc} csv={props.csv} canCreate={props.createable.includes(referenceForProvidedAPI(desc))} />)}
</div>
);

export const ClusterServiceVersionDetails: React.SFC<ClusterServiceVersionDetailsProps> = (props) => {
const {spec, metadata, status = {} as ClusterServiceVersionKind['status']} = props.obj;
Expand Down Expand Up @@ -269,12 +286,13 @@ export type ClusterServiceVersionListProps = {
export type CRDCardProps = {
crd: CRDDescription | APIServiceDefinition;
csv: ClusterServiceVersionKind;
kindObj: K8sKind;
canCreate: boolean;
};

export type CRDCardRowProps = {
crdDescs: (CRDDescription | APIServiceDefinition)[];
csv: ClusterServiceVersionKind;
createable: GroupVersionKind[];
};

export type CRDCardRowState = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export enum CSVConditionReason {
export enum InstallPlanApproval {
Automatic = 'Automatic',
Manual = 'Manual',
UpdateOnly = 'Update-Only',
}

export enum SubscriptionState {
Expand Down