Skip to content

Commit

Permalink
Include enabling/disabling default catalog sources in
Browse files Browse the repository at this point in the history
operator-hub detail view

Signed-off-by: Harish <hgovinda@redhat.com>
  • Loading branch information
harishsurf committed Dec 1, 2020
1 parent 33da36b commit d350d16
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { detailsPage } from '../../../integration-tests-cypress/views/details-page';
import { checkErrors } from '../../../integration-tests-cypress/support';
import { modal } from '../../../integration-tests-cypress/views/modal';

describe('Create namespace from install operators', () => {
before(() => {
cy.login();
});

afterEach(() => {
checkErrors();
});

after(() => {
cy.logout();
});

it('disables default catalog sources from operatorHub details page', () => {
cy.log('navigate to operatorHub page');
cy.visit(`/settings/cluster`);
cy.byLegacyTestID('horizontal-link-Global configuration').click();
cy.byLegacyTestID('OperatorHub').click();

// verfiy operatorHub details page is open
detailsPage.sectionHeaderShouldExist('OperatorHub details');

// Toggle default sources modal
const defaultSourceToBeToggled = 'redhat-operators';
cy.byTestID('Default Sources-details-item__edit-button').click();
modal.modalTitleShouldContain('Edit Default Sources');
cy.byTestID(defaultSourceToBeToggled)
.find('span.pf-c-switch__toggle')
.click();
modal.submit();

// Verify status change
cy.byTestID(`status_${defaultSourceToBeToggled}`).should('have.text', 'Disabled');

// switch the toggle back to previous state
cy.byTestID('Default Sources-details-item__edit-button').click();
modal.modalTitleShouldContain('Edit Default Sources');
cy.byTestID(defaultSourceToBeToggled)
.find('span.pf-c-switch__toggle')
.click();
modal.submit();

// Verify status change
cy.byTestID(`status_${defaultSourceToBeToggled}`).should('have.text', 'Enabled');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@ import { PackageManifestList } from './package-manifest';
import { deleteCatalogSourceModal } from './modals/delete-catalog-source-modal';
import { disableDefaultSourceModal } from './modals/disable-default-source-modal';
import { OperatorHubKind } from './operator-hub';

const DEFAULT_SOURCE_NAMESPACE = 'openshift-marketplace';
const catalogSourceModelReference = referenceForModel(CatalogSourceModel);
import { catalogSourceModelReference, DEFAULT_SOURCE_NAMESPACE } from '../const';

const deleteModal = (kind: K8sKind, catalogSource: CatalogSourceKind): KebabOption => ({
...Kebab.factory.Delete(kind, catalogSource),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import * as React from 'react';
import { k8sPatch } from '@console/internal/module/k8s';
import {
ModalTitle,
ModalBody,
createModalLauncher,
ModalComponentProps,
ModalSubmitFooter,
} from '@console/internal/components/factory/modal';
import { withHandlePromise, HandlePromiseProps } from '@console/internal/components/utils';
import { OperatorHubKind } from '../operator-hub';
import { Switch, Alert, Form, FormGroup } from '@patternfly/react-core';
import { OperatorHubModel } from '../../models';

const EditDefaultSourcesModal: React.FC<EditDefaultSourcesModalProps> = ({
cancel,
close,
operatorHub,
handlePromise,
errorMessage,
}) => {
// state to maintain user selection of toggle, maintained as an [] of {defaultCatalogSourceName: <booleanFlagForToggle>}
const [
userSelectedDefaultSourceToggleState,
setUserSelectedDefaultSourceToggleState,
] = React.useState(
(operatorHub.spec.sources ?? []).reduce(
(acc, source) => ({
...acc,
[source.name]: source.disabled,
}),
{},
),
);

const submit = React.useCallback(
(event: React.FormEvent<EventTarget>): void => {
event.preventDefault();
const patch = [
{
op: 'replace',
path: '/spec/sources',
value: Object.keys(userSelectedDefaultSourceToggleState).map((name) => ({
name,
disabled: userSelectedDefaultSourceToggleState[name],
})),
},
];
return handlePromise(k8sPatch(OperatorHubModel, operatorHub, patch), close);
},
[close, handlePromise, operatorHub, userSelectedDefaultSourceToggleState],
);

const onToggle = (sourceName, checked) => {
setUserSelectedDefaultSourceToggleState((currState) => ({
...currState,
[sourceName]: !checked,
}));
};

return (
<Form onSubmit={submit} isHorizontal isWidthLimited>
<div className="modal-content modal-content--no-inner-scroll">
<ModalTitle>Edit Default Sources</ModalTitle>
<ModalBody>
{operatorHub.status.sources.map((source) => {
return (
// Todo: Remove <div> after https://github.com/patternfly/patternfly-react/issues/5198 is fixed
<div key={source.name} className="row co-m-form-row pl-4">
<FormGroup fieldId={source.name} label={source.name} data-test={source.name}>
<Switch
id={source.name}
key={source.name}
label="Enabled"
labelOff="Disabled"
isChecked={!userSelectedDefaultSourceToggleState[source.name] ?? true}
onChange={(checked) => onToggle(source.name, checked)}
data-test={source.name}
/>
</FormGroup>
</div>
);
})}
{Object.values(userSelectedDefaultSourceToggleState).includes(true) && (
<Alert variant="warning" className="co-alert" title="Disable Catalog Source" isInline>
<p>
By disabling a default source, the operators it provides will no longer appear in
OperatorHub and any operator that has been installed from this source will no longer
receive updates until the source is re-enabled. Disabling the source will also
remove the corresponding OperatorSource and CatalogSource resources from the
cluster.
</p>
</Alert>
)}
</ModalBody>
<ModalSubmitFooter
errorMessage={errorMessage}
inProgress={false}
submitText="Save"
cancel={cancel}
/>
</div>
</Form>
);
};

export const editDefaultSourcesModal = createModalLauncher(
withHandlePromise(EditDefaultSourcesModal),
);

type EditDefaultSourcesModalProps = {
operatorHub: OperatorHubKind;
} & ModalComponentProps &
HandlePromiseProps;
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,65 @@ import {
SectionHeading,
ResourceSummary,
Kebab,
DetailsItem,
useAccessReview,
} from '@console/internal/components/utils';
import { CatalogSourceListPage, CatalogSourceListPageProps } from '../catalog-source';
import { OperatorHubKind } from '.';
import { OperatorHubModel } from '../../models';
import { editDefaultSourcesModal } from '../modals/edit-default-sources-modal';

const OperatorHubDetails: React.FC<OperatorHubDetailsProps> = ({ obj }) => {
const OperatorHubDetails: React.FC<OperatorHubDetailsProps> = ({ obj: operatorHub }) => {
const { t } = useTranslation();

const canEditDefaultSources = useAccessReview({
group: OperatorHubModel.apiGroup,
resource: OperatorHubModel.plural,
verb: 'update',
});

return (
<div className="co-m-pane__body">
<SectionHeading
text={t('operator-hub-details~{{resource}} details', { resource: OperatorHubModel.label })}
/>
<ResourceSummary resource={obj} podSelector="spec.podSelector" showNodeSelector={false} />
<div className="row">
<div className="col-sm-6 col-xs-12">
<ResourceSummary
resource={operatorHub}
podSelector="spec.podSelector"
showNodeSelector={false}
/>
</div>
<div className="col-sm-6 col-xs-12">
<div className="co-m-pane__details">
<DetailsItem
label="Default Sources"
obj={operatorHub}
path="status.sources"
canEdit={canEditDefaultSources}
onEdit={() => editDefaultSourcesModal({ operatorHub })}
editAsGroup
>
{operatorHub.status.sources.map((source, idx) => {
return (
<dl key={source.name}>
<DetailsItem
label={source.name}
obj={operatorHub}
path={`status.sources[${idx}]`}
>
<p data-test={`status_${source.name}`}>
{source.disabled ? 'Disabled' : 'Enabled'}
</p>
</DetailsItem>
</dl>
);
})}
</DetailsItem>
</div>
</div>
</div>
</div>
);
};
Expand Down
5 changes: 5 additions & 0 deletions frontend/packages/operator-lifecycle-manager/src/const.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { referenceForModel } from '@console/internal/module/k8s';
import { CatalogSourceModel } from './models';

export enum Flags {
OPERATOR_LIFECYCLE_MANAGER = 'OPERATOR_LIFECYCLE_MANAGER',
}

export const catalogSourceModelReference = referenceForModel(CatalogSourceModel);
export const DEFAULT_SOURCE_NAMESPACE = 'openshift-marketplace';
export const operatorTypeAnnotation = 'operators.operatorframework.io/operator-type';
export const nonStandaloneAnnotationValue = 'non-standalone';
23 changes: 18 additions & 5 deletions frontend/public/components/utils/details-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,20 @@ export const PropertyPath: React.FC<{ kind: string; path: string | string[] }> =
);
};

const EditButton: React.SFC<{ onClick: (e: React.MouseEvent<HTMLButtonElement>) => void }> = (
props,
) => (
<Button variant="link" isInline onClick={props.onClick}>
type EditButtonProps = {
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
label?: string;
};

const EditButton: React.FC<EditButtonProps> = (props) => (
<Button
variant="link"
isInline
onClick={props.onClick}
data-test={
`${props.label}` ? `${props.label}-details-item__edit-button` : 'details-item__edit-button'
}
>
{props.children}
<PencilAltIcon className="co-icon-space-l pf-c-button-icon--plain" />
</Button>
Expand All @@ -65,6 +75,7 @@ export const DetailsItem: React.FC<DetailsItemProps> = ({
const popoverContent: string = description ?? getPropertyDescription(model, path);
const value: React.ReactNode = children || _.get(obj, path, defaultValue);
const editable = onEdit && canEdit;

return hide ? null : (
<>
<dt
Expand Down Expand Up @@ -98,7 +109,9 @@ export const DetailsItem: React.FC<DetailsItemProps> = ({
<>
<SplitItem isFilled />
<SplitItem>
<EditButton onClick={onEdit}>{t('public~Edit')}</EditButton>
<EditButton label={label} onClick={onEdit}>
{t('public~Edit')}
</EditButton>
</SplitItem>
</>
)}
Expand Down

0 comments on commit d350d16

Please sign in to comment.