katello 4.5.0.rc1 → 4.5.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of katello might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/assets/javascripts/katello/hosts/activation_key_edit.js +9 -2
- data/app/controllers/katello/api/registry/registry_proxies_controller.rb +3 -0
- data/app/controllers/katello/api/v2/alternate_content_sources_bulk_actions_controller.rb +44 -0
- data/app/controllers/katello/api/v2/alternate_content_sources_controller.rb +29 -6
- data/app/controllers/katello/api/v2/content_view_components_controller.rb +1 -1
- data/app/controllers/katello/api/v2/content_view_repositories_controller.rb +1 -1
- data/app/controllers/katello/api/v2/repositories_controller.rb +2 -8
- data/app/lib/actions/katello/alternate_content_source/refresh.rb +27 -0
- data/app/lib/actions/katello/cdn_configuration/update.rb +1 -1
- data/app/lib/actions/katello/content_view/publish.rb +1 -1
- data/app/lib/actions/katello/organization/manifest_refresh.rb +1 -1
- data/app/lib/actions/pulp3/alternate_content_source/delete.rb +2 -2
- data/app/lib/actions/pulp3/alternate_content_source/delete_remote.rb +2 -2
- data/app/lib/actions/pulp3/alternate_content_source/refresh.rb +23 -0
- data/app/lib/actions/pulp3/alternate_content_source/update.rb +2 -2
- data/app/lib/actions/pulp3/alternate_content_source/update_remote.rb +2 -2
- data/app/lib/actions/pulp3/orchestration/alternate_content_source/create.rb +0 -2
- data/app/lib/actions/pulp3/orchestration/alternate_content_source/refresh.rb +15 -0
- data/app/lib/actions/pulp3/orchestration/alternate_content_source/update.rb +0 -2
- data/app/lib/actions/pulp3/repository/refresh_distribution.rb +1 -4
- data/app/lib/actions/pulp3/repository/save_artifact.rb +1 -1
- data/app/lib/actions/pulp3/repository/save_distribution_references.rb +0 -2
- data/app/models/katello/alternate_content_source.rb +5 -0
- data/app/services/katello/pulp3/alternate_content_source.rb +6 -0
- data/app/services/katello/pulp3/content_view_version/metadata_map.rb +1 -1
- data/app/services/katello/pulp3/repository.rb +29 -1
- data/app/views/katello/api/v2/alternate_content_sources/base.json.rabl +10 -1
- data/app/views/katello/api/v2/content_facet/show.json.rabl +12 -0
- data/app/views/katello/api/v2/repository_sets/show.json.rabl +4 -0
- data/config/routes/api/v2.rb +16 -4
- data/db/migrate/20220303160220_remove_duplicate_errata.rb +1 -1
- data/db/migrate/20220428203334_add_last_refreshed_to_katello_alternate_content_sources.rb +5 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/capsule-content/capsule-content.controller.js +1 -1
- data/lib/katello/permission_creator.rb +4 -2
- data/lib/katello/tasks/refresh_alternate_content_sources.rake +10 -0
- data/lib/katello/version.rb +1 -1
- data/webpack/components/Bookmark/index.js +22 -14
- data/webpack/components/Search/Search.js +4 -0
- data/webpack/components/Table/MainTable.scss +5 -1
- data/webpack/components/Table/TableWrapper.js +5 -1
- data/webpack/components/TypeAhead/TypeAhead.js +4 -0
- data/webpack/components/TypeAhead/pf4Search/TypeAheadSearch.js +2 -0
- data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/ChangeHostCVModal.js +2 -8
- data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/ContentViewDetailsCard.js +41 -11
- data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/HostCollectionsActions.js +2 -2
- data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/HostCollectionsCard.js +32 -13
- data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/__tests__/hostCollectionsCard.test.js +8 -0
- data/webpack/components/extensions/HostDetails/DetailsTabCards/RecentCommunicationCardExtensions.js +37 -0
- data/webpack/components/extensions/HostDetails/HostDetailsActions.js +11 -0
- data/webpack/components/extensions/HostDetails/Tabs/ContentTab/SecondaryTabsRoutes.js +4 -0
- data/webpack/components/extensions/HostDetails/Tabs/ContentTab/constants.js +2 -0
- data/webpack/components/extensions/HostDetails/Tabs/ContentTab/index.js +6 -1
- data/webpack/components/extensions/HostDetails/Tabs/ErrataTab/ErrataTab.js +120 -51
- data/webpack/components/extensions/HostDetails/Tabs/ModuleStreamsTab/ModuleStreamsTab.js +71 -37
- data/webpack/components/extensions/HostDetails/Tabs/PackagesTab/PackageInstallModal.js +4 -3
- data/webpack/components/extensions/HostDetails/Tabs/PackagesTab/PackagesTab.js +117 -40
- data/webpack/components/extensions/HostDetails/Tabs/RemoteExecutionActions.js +25 -3
- data/webpack/components/extensions/HostDetails/Tabs/RemoteExecutionHooks.js +85 -0
- data/webpack/components/extensions/HostDetails/Tabs/RepositorySetsTab/RepositorySetsTab.js +87 -33
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/EnableTracerModal.js +14 -7
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/HostTracesActions.js +2 -1
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/TracesEnabler.js +104 -0
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/TracesTab.js +92 -51
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/errataTab.test.js +13 -23
- data/webpack/components/extensions/HostDetails/Tabs/{ModuleStreamsTab/__tests__/modules.fixtures.json → __tests__/moduleStreams.fixtures.json} +0 -0
- data/webpack/components/extensions/HostDetails/Tabs/{ModuleStreamsTab/__tests__ → __tests__}/moduleStreamsTab.test.js +13 -6
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/packageInstallModal.test.js +21 -15
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/packagesTab.test.js +8 -0
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/repositorySets.fixtures.json +4 -1
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/repositorySetsTab.test.js +26 -0
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/tracesTab.test.js +7 -4
- data/webpack/components/extensions/HostDetails/hostDetailsHelpers.js +18 -0
- data/webpack/global_index.js +2 -2
- data/webpack/redux/actions/RedHatRepositories/helpers.js +5 -1
- data/webpack/scenes/AlternateContentSources/ACSActions.js +13 -1
- data/webpack/scenes/AlternateContentSources/ACSConstants.js +14 -0
- data/webpack/scenes/AlternateContentSources/ACSSelectors.js +10 -1
- data/webpack/scenes/AlternateContentSources/Create/ACSCreateContext.js +4 -0
- data/webpack/scenes/AlternateContentSources/Create/ACSCreateWizard.js +160 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/ACSCreateFinish.js +79 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/ACSCredentials.js +199 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/ACSReview.js +104 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/ACSSmartProxies.js +41 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/AcsUrlPaths.js +71 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/NameACS.js +57 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/SelectSource.js +77 -0
- data/webpack/scenes/AlternateContentSources/Create/__tests__/acsCreate.test.js +149 -0
- data/webpack/scenes/AlternateContentSources/Create/__tests__/acsCreateData.fixtures.json +3 -0
- data/webpack/scenes/AlternateContentSources/Create/__tests__/contentCredentials.fixtures.json +69 -0
- data/webpack/scenes/AlternateContentSources/Create/__tests__/smartProxy.fixtures.json +65 -0
- data/webpack/scenes/AlternateContentSources/MainTable/ACSTable.js +33 -23
- data/webpack/scenes/ContentCredentials/ContentCredentialSelectors.js +4 -1
- data/webpack/scenes/ContentViews/Create/CreateContentViewForm.js +2 -2
- data/webpack/scenes/ContentViews/Create/__tests__/createContentView.test.js +1 -1
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentContentViewAddModal.js +1 -1
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentContentViewBulkAddModal.js +2 -2
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/contentViewComponents.test.js +4 -4
- data/webpack/scenes/ContentViews/Details/ContentViewInfo.js +1 -1
- data/webpack/scenes/ContentViews/__tests__/contentViewPage.test.js +4 -4
- data/webpack/scenes/ContentViews/components/ContentViewIcon.js +1 -1
- data/webpack/scenes/ContentViews/components/ContentViewsCounter.js +1 -1
- data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.js +1 -1
- data/webpack/scenes/ContentViews/expansions/DetailsExpansion.js +2 -2
- data/webpack/scenes/ContentViews/expansions/RelatedContentViewComponentsModal.js +2 -2
- data/webpack/scenes/ContentViews/expansions/__tests__/contentViewComponentsModal.test.js +1 -1
- data/webpack/scenes/RedHatRepositories/components/Search.js +4 -4
- data/webpack/scenes/SmartProxy/SmartProxyContentActions.js +9 -2
- data/webpack/scenes/SmartProxy/SmartProxyContentConstants.js +1 -1
- data/webpack/scenes/SmartProxy/SmartProxyContentSelectors.js +10 -1
- data/webpack/scenes/Tasks/helpers.js +30 -3
- metadata +34 -14
- data/db/seeds.d/107-enable_dynflow.rb +0 -8
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/EnableTracerEmptyState.js +0 -42
@@ -12,13 +12,12 @@ import {
|
|
12
12
|
} from '@patternfly/react-core';
|
13
13
|
import { CaretDownIcon } from '@patternfly/react-icons';
|
14
14
|
import { translate as __ } from 'foremanReact/common/I18n';
|
15
|
-
import { useSelector
|
15
|
+
import { useSelector } from 'react-redux';
|
16
16
|
import { selectAPIResponse } from 'foremanReact/redux/API/APISelectors';
|
17
|
-
import { installTracerPackage } from './HostTracesActions';
|
18
17
|
import { katelloPackageInstallUrl } from '../customizedRexUrlHelpers';
|
19
18
|
import { KATELLO_TRACER_PACKAGE } from './HostTracesConstants';
|
20
19
|
|
21
|
-
const EnableTracerModal = ({ isOpen, setIsOpen }) => {
|
20
|
+
const EnableTracerModal = ({ isOpen, setIsOpen, triggerJobStart }) => {
|
22
21
|
const title = __('Enable Tracer');
|
23
22
|
const body = __('Enabling will install the katello-host-tools-tracer package on the host.');
|
24
23
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
@@ -29,16 +28,19 @@ const EnableTracerModal = ({ isOpen, setIsOpen }) => {
|
|
29
28
|
__('via customized remote execution'),
|
30
29
|
];
|
31
30
|
const [selectedOption, setSelectedOption] = useState(dropdownOptions[0]);
|
32
|
-
const dispatch = useDispatch();
|
33
31
|
const hostDetails = useSelector(state => selectAPIResponse(state, 'HOST_DETAILS'));
|
34
32
|
const { name: hostname } = hostDetails;
|
35
33
|
const handleSelect = () => {
|
36
34
|
setIsDropdownOpen(false);
|
37
35
|
};
|
38
|
-
const
|
39
|
-
dispatch(installTracerPackage({ hostname }));
|
36
|
+
const handleClose = () => {
|
40
37
|
setIsOpen(false);
|
41
38
|
};
|
39
|
+
const enableTracer = () => {
|
40
|
+
setButtonLoading(true);
|
41
|
+
triggerJobStart();
|
42
|
+
handleClose();
|
43
|
+
};
|
42
44
|
|
43
45
|
const dropdownItems = dropdownOptions.map(text => (
|
44
46
|
<DropdownItem key={`option_${text}`} onClick={() => setSelectedOption(text)}>{text}</DropdownItem>
|
@@ -54,6 +56,8 @@ const EnableTracerModal = ({ isOpen, setIsOpen }) => {
|
|
54
56
|
key="enable_button"
|
55
57
|
type="submit"
|
56
58
|
variant="primary"
|
59
|
+
isLoading={buttonLoading}
|
60
|
+
isDisabled={buttonLoading}
|
57
61
|
onClick={enableTracer}
|
58
62
|
>
|
59
63
|
{title}
|
@@ -65,6 +69,7 @@ const EnableTracerModal = ({ isOpen, setIsOpen }) => {
|
|
65
69
|
key="enable_button"
|
66
70
|
component="a"
|
67
71
|
isLoading={buttonLoading}
|
72
|
+
isDisabled={buttonLoading}
|
68
73
|
onClick={() => setButtonLoading(true)}
|
69
74
|
variant="primary"
|
70
75
|
href={customizedRexUrl}
|
@@ -80,7 +85,7 @@ const EnableTracerModal = ({ isOpen, setIsOpen }) => {
|
|
80
85
|
title={title}
|
81
86
|
width="28em"
|
82
87
|
isOpen={isOpen}
|
83
|
-
onClose={
|
88
|
+
onClose={handleClose}
|
84
89
|
actions={[
|
85
90
|
getEnableTracerButton(),
|
86
91
|
<Button key="cancel_button" variant="link" onClick={() => setIsOpen(false)}>{__('Cancel')}</Button>,
|
@@ -96,6 +101,7 @@ const EnableTracerModal = ({ isOpen, setIsOpen }) => {
|
|
96
101
|
id="toggle-enable-tracer-modal-dropdown"
|
97
102
|
onToggle={toggleDropdownOpen}
|
98
103
|
toggleIndicator={CaretDownIcon}
|
104
|
+
isDisabled={buttonLoading}
|
99
105
|
>
|
100
106
|
{selectedOption}
|
101
107
|
</DropdownToggle>
|
@@ -114,6 +120,7 @@ const EnableTracerModal = ({ isOpen, setIsOpen }) => {
|
|
114
120
|
EnableTracerModal.propTypes = {
|
115
121
|
isOpen: PropTypes.bool.isRequired,
|
116
122
|
setIsOpen: PropTypes.func.isRequired,
|
123
|
+
triggerJobStart: PropTypes.func.isRequired,
|
117
124
|
};
|
118
125
|
|
119
126
|
export default EnableTracerModal;
|
@@ -13,7 +13,8 @@ export const getHostTraces = (hostId, params) => get({
|
|
13
13
|
params,
|
14
14
|
});
|
15
15
|
|
16
|
-
export const installTracerPackage = ({ hostname }) => installPackage({
|
16
|
+
export const installTracerPackage = ({ hostname, handleSuccess }) => installPackage({
|
17
17
|
hostname,
|
18
18
|
packageName: KATELLO_TRACER_PACKAGE,
|
19
|
+
handleSuccess,
|
19
20
|
});
|
@@ -0,0 +1,104 @@
|
|
1
|
+
import React, { useState } from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import {
|
4
|
+
EmptyState,
|
5
|
+
EmptyStateIcon,
|
6
|
+
EmptyStateBody,
|
7
|
+
Title,
|
8
|
+
EmptyStateVariant,
|
9
|
+
Button,
|
10
|
+
Flex,
|
11
|
+
FlexItem,
|
12
|
+
Spinner,
|
13
|
+
} from '@patternfly/react-core';
|
14
|
+
import { WrenchIcon } from '@patternfly/react-icons';
|
15
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
16
|
+
import { urlBuilder } from 'foremanReact/common/urlHelpers';
|
17
|
+
import EnableTracerModal from './EnableTracerModal';
|
18
|
+
import { useRexJobPolling } from '../RemoteExecutionHooks';
|
19
|
+
import { installTracerPackage } from './HostTracesActions';
|
20
|
+
import { getHostDetails } from '../../HostDetailsActions';
|
21
|
+
|
22
|
+
const EnableTracerButton = ({ setEnableTracerModalOpen, pollingStarted }) => (
|
23
|
+
<Button
|
24
|
+
onClick={() => setEnableTracerModalOpen(true)}
|
25
|
+
isDisabled={pollingStarted}
|
26
|
+
>
|
27
|
+
{__('Enable Traces')}
|
28
|
+
</Button>
|
29
|
+
);
|
30
|
+
|
31
|
+
const ViewTaskButton = ({ jobId }) => (
|
32
|
+
<Button
|
33
|
+
component="a"
|
34
|
+
href={urlBuilder('job_invocations', '', jobId)}
|
35
|
+
variant="secondary"
|
36
|
+
isDisabled={!jobId}
|
37
|
+
>
|
38
|
+
{__('View the job')}
|
39
|
+
</Button>
|
40
|
+
);
|
41
|
+
|
42
|
+
ViewTaskButton.propTypes = {
|
43
|
+
jobId: PropTypes.number,
|
44
|
+
};
|
45
|
+
ViewTaskButton.defaultProps = {
|
46
|
+
jobId: null,
|
47
|
+
};
|
48
|
+
|
49
|
+
EnableTracerButton.propTypes = {
|
50
|
+
setEnableTracerModalOpen: PropTypes.func.isRequired,
|
51
|
+
pollingStarted: PropTypes.bool.isRequired,
|
52
|
+
};
|
53
|
+
|
54
|
+
const TracesEnabler = ({ hostname }) => {
|
55
|
+
const title = __('Traces are not enabled');
|
56
|
+
const enablingTitle = __('Traces are being enabled');
|
57
|
+
const body = __('Traces help administrators identify applications that need to be restarted after a system is patched.');
|
58
|
+
const [enableTracerModalOpen, setEnableTracerModalOpen] = useState(false);
|
59
|
+
const initialAction = installTracerPackage({ hostname });
|
60
|
+
const successAction = getHostDetails({ hostname });
|
61
|
+
const {
|
62
|
+
pollingStarted,
|
63
|
+
rexJobId,
|
64
|
+
triggerJobStart,
|
65
|
+
} = useRexJobPolling(initialAction, successAction);
|
66
|
+
|
67
|
+
return (
|
68
|
+
<EmptyState variant={EmptyStateVariant.small}>
|
69
|
+
{pollingStarted ?
|
70
|
+
<Spinner /> :
|
71
|
+
<EmptyStateIcon icon={WrenchIcon} />
|
72
|
+
}
|
73
|
+
<Title headingLevel="h2" size="lg">
|
74
|
+
{pollingStarted ? enablingTitle : title}
|
75
|
+
</Title>
|
76
|
+
<EmptyStateBody>
|
77
|
+
<Flex direction={{ default: 'column' }}>
|
78
|
+
<FlexItem>{body}</FlexItem>
|
79
|
+
<FlexItem>
|
80
|
+
{pollingStarted ?
|
81
|
+
<ViewTaskButton jobId={rexJobId} /> :
|
82
|
+
<EnableTracerButton {...{
|
83
|
+
setEnableTracerModalOpen,
|
84
|
+
pollingStarted,
|
85
|
+
}}
|
86
|
+
/>
|
87
|
+
}
|
88
|
+
</FlexItem>
|
89
|
+
</Flex>
|
90
|
+
</EmptyStateBody>
|
91
|
+
<EnableTracerModal
|
92
|
+
isOpen={enableTracerModalOpen}
|
93
|
+
setIsOpen={setEnableTracerModalOpen}
|
94
|
+
triggerJobStart={triggerJobStart}
|
95
|
+
/>
|
96
|
+
</EmptyState>
|
97
|
+
);
|
98
|
+
};
|
99
|
+
|
100
|
+
TracesEnabler.propTypes = {
|
101
|
+
hostname: PropTypes.string.isRequired,
|
102
|
+
};
|
103
|
+
|
104
|
+
export default TracesEnabler;
|
@@ -1,14 +1,14 @@
|
|
1
1
|
import React, { useState, useCallback } from 'react';
|
2
2
|
import { FormattedMessage } from 'react-intl';
|
3
3
|
import {
|
4
|
-
Skeleton,
|
5
|
-
DropdownItem,
|
4
|
+
Skeleton, Split, SplitItem, ActionList, ActionListItem, Dropdown,
|
5
|
+
DropdownItem, DropdownToggle, DropdownToggleAction,
|
6
6
|
} from '@patternfly/react-core';
|
7
7
|
import { translate as __ } from 'foremanReact/common/I18n';
|
8
8
|
import { TableVariant, Thead, Tbody, Tr, Th, Td } from '@patternfly/react-table';
|
9
|
-
import { useSelector
|
9
|
+
import { useSelector } from 'react-redux';
|
10
10
|
import { selectAPIResponse } from 'foremanReact/redux/API/APISelectors';
|
11
|
-
import
|
11
|
+
import TracesEnabler from './TracesEnabler';
|
12
12
|
import TableWrapper from '../../../../Table/TableWrapper';
|
13
13
|
import { useBulkSelect, useTableSort, useUrlParams } from '../../../../Table/TableHooks';
|
14
14
|
import { getHostTraces } from './HostTracesActions';
|
@@ -18,24 +18,33 @@ import { resolveTraceUrl } from '../customizedRexUrlHelpers';
|
|
18
18
|
import './TracesTab.scss';
|
19
19
|
import hostIdNotReady from '../../HostDetailsActions';
|
20
20
|
import SortableColumnHeaders from '../../../../Table/components/SortableColumnHeaders';
|
21
|
+
import { useRexJobPolling } from '../RemoteExecutionHooks';
|
22
|
+
import { hasRequiredPermissions as can,
|
23
|
+
missingRequiredPermissions as cannot,
|
24
|
+
userPermissionsFromHostDetails } from '../../hostDetailsHelpers';
|
25
|
+
|
26
|
+
const invokeRexJobs = ['create_job_invocations'];
|
27
|
+
const createBookmarks = ['create_bookmarks'];
|
21
28
|
|
22
29
|
const TracesTab = () => {
|
23
30
|
const hostDetails = useSelector(state => selectAPIResponse(state, 'HOST_DETAILS'));
|
24
|
-
const dispatch = useDispatch();
|
25
31
|
const {
|
26
32
|
id: hostId,
|
27
33
|
name: hostname,
|
28
34
|
content_facet_attributes: contentFacetAttributes,
|
29
35
|
} = hostDetails;
|
36
|
+
const showActions = can(invokeRexJobs, userPermissionsFromHostDetails({ hostDetails }));
|
30
37
|
const showEnableTracer = (contentFacetAttributes?.katello_tracer_installed === false);
|
31
|
-
const emptyContentTitle = __('No applications to restart');
|
32
|
-
const
|
38
|
+
const emptyContentTitle = showActions ? __('No applications to restart') : __('Traces not available');
|
39
|
+
const tracesNotAvailBody = showEnableTracer ? __('Traces may be enabled by a user with the appropriate permissions.') :
|
40
|
+
__('Traces will be shown here to a user with the appropriate permissions.');
|
41
|
+
const emptyContentBody = showActions ? (<FormattedMessage
|
33
42
|
id="traces-happy-empty"
|
34
43
|
values={{
|
35
44
|
pkgLink: <a href="#/Content/packages?status=Upgradable">{__('installing or updating packages')}</a>,
|
36
45
|
}}
|
37
46
|
defaultMessage={__('Traces may be listed here after {pkgLink}.')}
|
38
|
-
/>);
|
47
|
+
/>) : tracesNotAvailBody;
|
39
48
|
const emptySearchTitle = __('No matching traces found');
|
40
49
|
const emptySearchBody = __('Try changing your search settings.');
|
41
50
|
const errorSearchTitle = __('Problem searching traces');
|
@@ -74,6 +83,26 @@ const TracesTab = () => {
|
|
74
83
|
initialSearchQuery: searchParam || '',
|
75
84
|
});
|
76
85
|
|
86
|
+
const BulkRestartTracesAction = () => resolveTraces({
|
87
|
+
hostname, search: fetchBulkParams(),
|
88
|
+
});
|
89
|
+
const {
|
90
|
+
triggerJobStart: triggerBulkRestart, lastCompletedJob: lastCompletedBulkRestart,
|
91
|
+
isPolling: isBulkRestartInProgress,
|
92
|
+
} = useRexJobPolling(BulkRestartTracesAction);
|
93
|
+
|
94
|
+
const restartTraceAction = id => resolveTraces({
|
95
|
+
hostname,
|
96
|
+
search: tracesSearchQuery(id),
|
97
|
+
});
|
98
|
+
|
99
|
+
const {
|
100
|
+
triggerJobStart: triggerAppRestart, lastCompletedJob: lastCompletedAppRestart,
|
101
|
+
isPolling: isAppRestartInProgress,
|
102
|
+
} = useRexJobPolling(restartTraceAction);
|
103
|
+
|
104
|
+
const actionInProgress = (isBulkRestartInProgress || isAppRestartInProgress);
|
105
|
+
|
77
106
|
const fetchItems = useCallback(
|
78
107
|
params =>
|
79
108
|
(hostId ? getHostTraces(hostId, { ...apiSortParams, ...params }) : hostIdNotReady),
|
@@ -81,23 +110,19 @@ const TracesTab = () => {
|
|
81
110
|
);
|
82
111
|
|
83
112
|
const onBulkRestartApp = () => {
|
84
|
-
|
85
|
-
hostname, search: fetchBulkParams(),
|
86
|
-
}));
|
113
|
+
triggerBulkRestart();
|
87
114
|
selectNone();
|
88
|
-
const params = { page: meta.page, per_page: meta.per_page, search: meta.search };
|
89
|
-
dispatch(getHostTraces(hostId, params));
|
90
115
|
};
|
91
116
|
|
92
|
-
const onRestartApp = id =>
|
93
|
-
hostname,
|
94
|
-
search: tracesSearchQuery(id),
|
95
|
-
}));
|
117
|
+
const onRestartApp = id => triggerAppRestart(id);
|
96
118
|
|
97
119
|
const bulkCustomizedRexUrl = () => resolveTraceUrl({
|
98
120
|
hostname, search: (selectedCount > 0) ? fetchBulkParams() : '',
|
99
121
|
});
|
100
122
|
|
123
|
+
const readOnlyBookmarks =
|
124
|
+
cannot(createBookmarks, userPermissionsFromHostDetails({ hostDetails }));
|
125
|
+
|
101
126
|
const dropdownItems = [
|
102
127
|
<DropdownItem isDisabled={selectedCount === 0} aria-label="bulk_rex" key="bulk_rex" component="button" onClick={onBulkRestartApp}>
|
103
128
|
{__('Restart via remote execution')}
|
@@ -107,24 +132,28 @@ const TracesTab = () => {
|
|
107
132
|
</DropdownItem>,
|
108
133
|
];
|
109
134
|
|
110
|
-
const actionButtons = (
|
135
|
+
const actionButtons = showActions ? (
|
111
136
|
<Split hasGutter>
|
112
137
|
<SplitItem>
|
113
138
|
<ActionList isIconList>
|
114
|
-
<ActionListItem>
|
115
|
-
<Button
|
116
|
-
variant="secondary"
|
117
|
-
isDisabled={selectedCount === 0}
|
118
|
-
onClick={onBulkRestartApp}
|
119
|
-
>
|
120
|
-
{__('Restart app')}
|
121
|
-
</Button>
|
122
|
-
</ActionListItem>
|
123
139
|
<ActionListItem>
|
124
140
|
<Dropdown
|
125
|
-
|
141
|
+
aria-label="bulk_actions_dropdown"
|
142
|
+
toggle={
|
143
|
+
<DropdownToggle
|
144
|
+
aria-label="bulk_actions"
|
145
|
+
splitButtonItems={[
|
146
|
+
<DropdownToggleAction key="action" onClick={onBulkRestartApp}>
|
147
|
+
{__('Restart app')}
|
148
|
+
</DropdownToggleAction>,
|
149
|
+
]}
|
150
|
+
isDisabled={selectedCount === 0}
|
151
|
+
splitButtonVariant="action"
|
152
|
+
toggleVariant="primary"
|
153
|
+
onToggle={toggleBulkAction}
|
154
|
+
/>
|
155
|
+
}
|
126
156
|
isOpen={isBulkActionOpen}
|
127
|
-
isPlain
|
128
157
|
dropdownItems={dropdownItems}
|
129
158
|
/>
|
130
159
|
</ActionListItem>
|
@@ -132,9 +161,9 @@ const TracesTab = () => {
|
|
132
161
|
</SplitItem>
|
133
162
|
</Split>
|
134
163
|
|
135
|
-
);
|
164
|
+
) : null;
|
136
165
|
const status = useSelector(state => selectHostTracesStatus(state));
|
137
|
-
if (showEnableTracer) return <
|
166
|
+
if (showEnableTracer && showActions) return <TracesEnabler hostname={hostname} />;
|
138
167
|
|
139
168
|
if (!hostId) return <Skeleton />;
|
140
169
|
|
@@ -159,16 +188,18 @@ const TracesTab = () => {
|
|
159
188
|
actionButtons,
|
160
189
|
}
|
161
190
|
}
|
162
|
-
happyEmptyContent
|
191
|
+
happyEmptyContent={showActions}
|
163
192
|
ouiaId="host-traces-table"
|
164
193
|
metadata={meta}
|
165
194
|
bookmarkController="katello_host_tracers"
|
195
|
+
readOnlyBookmarks={readOnlyBookmarks}
|
166
196
|
autocompleteEndpoint={`/hosts/${hostId}/traces/auto_complete_search`}
|
167
197
|
foremanApiAutoComplete
|
168
198
|
rowsCount={results?.length}
|
169
199
|
variant={TableVariant.compact}
|
170
|
-
|
171
|
-
|
200
|
+
additionalListeners={[activeSortColumn, activeSortDirection,
|
201
|
+
lastCompletedAppRestart, lastCompletedBulkRestart]}
|
202
|
+
displaySelectAllCheckbox={showActions}
|
172
203
|
{...selectAll}
|
173
204
|
>
|
174
205
|
<Thead>
|
@@ -191,8 +222,11 @@ const TracesTab = () => {
|
|
191
222
|
app_type: appType,
|
192
223
|
} = result;
|
193
224
|
const resolveDisabled = !isSelectable(id);
|
225
|
+
let disabledReason;
|
226
|
+
if (resolveDisabled) disabledReason = __('Traces that require logout cannot be restarted remotely');
|
227
|
+
if (actionInProgress) disabledReason = __('A remote execution job is in progress');
|
194
228
|
let rowDropdownItems = [
|
195
|
-
{ title: 'Restart via remote execution', onClick: () => onRestartApp(id) },
|
229
|
+
{ title: 'Restart via remote execution', onClick: () => onRestartApp(id), isDisabled: actionInProgress },
|
196
230
|
{
|
197
231
|
component: 'a', href: resolveTraceUrl({ hostname, search: tracesSearchQuery(id) }), title: 'Restart via customized remote execution',
|
198
232
|
},
|
@@ -204,25 +238,32 @@ const TracesTab = () => {
|
|
204
238
|
}
|
205
239
|
return (
|
206
240
|
<Tr key={id} >
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
241
|
+
{showActions ? (
|
242
|
+
<Td
|
243
|
+
select={{
|
244
|
+
disable: actionInProgress || resolveDisabled,
|
245
|
+
props: {
|
246
|
+
'aria-label': `check-${application}`,
|
247
|
+
},
|
248
|
+
isSelected: isSelected(id),
|
249
|
+
onSelect: (event, selected) => selectOne(selected, id),
|
250
|
+
rowIndex,
|
251
|
+
variant: 'checkbox',
|
252
|
+
}}
|
253
|
+
title={disabledReason}
|
254
|
+
/>
|
255
|
+
) : <Td> </Td>
|
256
|
+
}
|
218
257
|
<Td>{application}</Td>
|
219
258
|
<Td>{appType}</Td>
|
220
259
|
<Td>{helper}</Td>
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
260
|
+
{showActions && (
|
261
|
+
<Td
|
262
|
+
actions={{
|
263
|
+
items: rowDropdownItems,
|
264
|
+
}}
|
265
|
+
/>
|
266
|
+
)}
|
226
267
|
</Tr>
|
227
268
|
);
|
228
269
|
})
|
@@ -10,6 +10,14 @@ import mockErrataData from './errata.fixtures.json';
|
|
10
10
|
import mockResolveErrataTask from './resolveErrata.fixtures.json';
|
11
11
|
import mockBookmarkData from './bookmarks.fixtures.json';
|
12
12
|
|
13
|
+
jest.mock('../../hostDetailsHelpers', () => ({
|
14
|
+
...jest.requireActual('../../hostDetailsHelpers'),
|
15
|
+
userPermissionsFromHostDetails: () => ({
|
16
|
+
create_job_invocations: true,
|
17
|
+
edit_hosts: true,
|
18
|
+
}),
|
19
|
+
}));
|
20
|
+
|
13
21
|
const contentFacetAttributes = {
|
14
22
|
id: 11,
|
15
23
|
uuid: 'e5761ea3-4117-4ecf-83d0-b694f99b389e',
|
@@ -875,7 +883,7 @@ test('Can bulk apply via katello agent', async (done) => {
|
|
875
883
|
getByLabelText('Select row 0').click();
|
876
884
|
getByLabelText('Select row 1').click();
|
877
885
|
|
878
|
-
const actionMenu = getByLabelText('
|
886
|
+
const actionMenu = getByLabelText('expand_errata_toggle');
|
879
887
|
actionMenu.click();
|
880
888
|
const viaAction = queryByText('Apply via Katello agent');
|
881
889
|
expect(viaAction).toBeInTheDocument();
|
@@ -923,7 +931,7 @@ test('Can select all, exclude and bulk apply via katello agent', async (done) =>
|
|
923
931
|
|
924
932
|
getByLabelText('Select row 0').click(); // deselect
|
925
933
|
|
926
|
-
const actionMenu = getByLabelText('
|
934
|
+
const actionMenu = getByLabelText('expand_errata_toggle');
|
927
935
|
actionMenu.click();
|
928
936
|
const viaAction = queryByText('Apply via Katello agent');
|
929
937
|
expect(viaAction).toBeInTheDocument();
|
@@ -949,11 +957,6 @@ test('Apply button chooses remote execution', async (done) => {
|
|
949
957
|
.query(defaultQuery)
|
950
958
|
.reply(200, mockErrata);
|
951
959
|
|
952
|
-
const scope1 = nockInstance
|
953
|
-
.get(hostErrata)
|
954
|
-
.query(baseQuery)
|
955
|
-
.reply(200, mockErrata);
|
956
|
-
|
957
960
|
const resolveErrataScope = nockInstance
|
958
961
|
.post(jobInvocations)
|
959
962
|
.reply(201, mockResolveErrataTask);
|
@@ -974,7 +977,6 @@ test('Apply button chooses remote execution', async (done) => {
|
|
974
977
|
|
975
978
|
assertNockRequest(autocompleteScope);
|
976
979
|
assertNockRequest(resolveErrataScope);
|
977
|
-
assertNockRequest(scope1);
|
978
980
|
assertNockRequest(scope, done);
|
979
981
|
});
|
980
982
|
|
@@ -987,11 +989,6 @@ test('Can bulk apply via remote execution', async (done) => {
|
|
987
989
|
.query(defaultQuery)
|
988
990
|
.reply(200, mockErrata);
|
989
991
|
|
990
|
-
const scope1 = nockInstance
|
991
|
-
.get(hostErrata)
|
992
|
-
.query(baseQuery)
|
993
|
-
.reply(200, mockErrata);
|
994
|
-
|
995
992
|
// eslint-disable-next-line camelcase
|
996
993
|
const jobInvocationBody = ({ job_invocation: { inputs, feature, search_query } }) =>
|
997
994
|
inputs[ERRATA_SEARCH_QUERY] === `errata_id ^ (${results[0].errata_id},${results[1].errata_id})` &&
|
@@ -1013,7 +1010,7 @@ test('Can bulk apply via remote execution', async (done) => {
|
|
1013
1010
|
getByLabelText('Select row 0').click();
|
1014
1011
|
getByLabelText('Select row 1').click();
|
1015
1012
|
|
1016
|
-
const actionMenu = getByLabelText('
|
1013
|
+
const actionMenu = getByLabelText('expand_errata_toggle');
|
1017
1014
|
actionMenu.click();
|
1018
1015
|
const viaRexAction = queryByText('Apply via remote execution');
|
1019
1016
|
expect(viaRexAction).toBeInTheDocument();
|
@@ -1021,7 +1018,6 @@ test('Can bulk apply via remote execution', async (done) => {
|
|
1021
1018
|
|
1022
1019
|
assertNockRequest(autocompleteScope);
|
1023
1020
|
assertNockRequest(resolveErrataScope);
|
1024
|
-
assertNockRequest(scope1);
|
1025
1021
|
assertNockRequest(scope, done);
|
1026
1022
|
});
|
1027
1023
|
|
@@ -1036,11 +1032,6 @@ test('Can select all, exclude and bulk apply via remote execution', async (done)
|
|
1036
1032
|
.query(defaultQuery)
|
1037
1033
|
.reply(200, mockErrata);
|
1038
1034
|
|
1039
|
-
const scope1 = nockInstance
|
1040
|
-
.get(hostErrata)
|
1041
|
-
.query(baseQuery)
|
1042
|
-
.reply(200, mockErrata);
|
1043
|
-
|
1044
1035
|
const jobInvocationBody = ({ job_invocation: { inputs } }) =>
|
1045
1036
|
inputs[ERRATA_SEARCH_QUERY] === `errata_id !^ (${results[0].errata_id})`;
|
1046
1037
|
|
@@ -1059,7 +1050,7 @@ test('Can select all, exclude and bulk apply via remote execution', async (done)
|
|
1059
1050
|
|
1060
1051
|
getByLabelText('Select row 0').click(); // de select
|
1061
1052
|
|
1062
|
-
const actionMenu = getByLabelText('
|
1053
|
+
const actionMenu = getByLabelText('expand_errata_toggle');
|
1063
1054
|
actionMenu.click();
|
1064
1055
|
const viaRexAction = queryByText('Apply via remote execution');
|
1065
1056
|
expect(viaRexAction).toBeInTheDocument();
|
@@ -1067,7 +1058,6 @@ test('Can select all, exclude and bulk apply via remote execution', async (done)
|
|
1067
1058
|
|
1068
1059
|
assertNockRequest(autocompleteScope);
|
1069
1060
|
assertNockRequest(resolveErrataScope);
|
1070
|
-
assertNockRequest(scope1);
|
1071
1061
|
assertNockRequest(scope, done);
|
1072
1062
|
});
|
1073
1063
|
|
@@ -1091,7 +1081,7 @@ test('Can apply errata in bulk via customized remote execution', async (done) =>
|
|
1091
1081
|
getByLabelText('Select row 1').click();
|
1092
1082
|
const errata = `${results[0].errata_id},${results[1].errata_id}`;
|
1093
1083
|
const feature = REX_FEATURES.KATELLO_HOST_ERRATA_INSTALL_BY_SEARCH;
|
1094
|
-
const actionMenu = getByLabelText('
|
1084
|
+
const actionMenu = getByLabelText('expand_errata_toggle');
|
1095
1085
|
actionMenu.click();
|
1096
1086
|
const viaRexAction = queryByText('Apply via customized remote execution');
|
1097
1087
|
expect(viaRexAction).toBeInTheDocument();
|
File without changes
|
@@ -1,12 +1,19 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { act } from 'react-test-renderer';
|
3
3
|
import { renderWithRedux, patientlyWaitFor, within, fireEvent } from 'react-testing-lib-wrapper';
|
4
|
-
import { nockInstance, assertNockRequest, mockForemanAutocomplete, mockSetting } from '
|
5
|
-
import {
|
6
|
-
import
|
7
|
-
import
|
8
|
-
import { MODULE_STREAMS_KEY } from '
|
9
|
-
import
|
4
|
+
import { nockInstance, assertNockRequest, mockForemanAutocomplete, mockSetting } from '../../../../../test-utils/nockWrapper';
|
5
|
+
import { ModuleStreamsTab } from '../ModuleStreamsTab/ModuleStreamsTab.js';
|
6
|
+
import mockModuleStreams from './moduleStreams.fixtures.json';
|
7
|
+
import mockBookmarkData from './bookmarks.fixtures.json';
|
8
|
+
import { MODULE_STREAMS_KEY } from '../../../../../scenes/ModuleStreams/ModuleStreamsConstants';
|
9
|
+
import { foremanApi } from '../../../../../services/api';
|
10
|
+
|
11
|
+
jest.mock('../../hostDetailsHelpers', () => ({
|
12
|
+
...jest.requireActual('../../hostDetailsHelpers'),
|
13
|
+
userPermissionsFromHostDetails: () => ({
|
14
|
+
create_job_invocations: true,
|
15
|
+
}),
|
16
|
+
}));
|
10
17
|
|
11
18
|
const moduleBookmarks = foremanApi.getApiUrl('/bookmarks?search=controller%3Dkatello_host_available_module_streams');
|
12
19
|
|