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
@@ -4,9 +4,17 @@ import { nockInstance, assertNockRequest, mockForemanAutocomplete, mockSetting }
|
|
4
4
|
import katelloApi, { foremanApi } from '../../../../../services/api';
|
5
5
|
import mockPackagesData from './yumInstallablePackages.fixtures.json';
|
6
6
|
import PackageInstallModal from '../PackagesTab/PackageInstallModal';
|
7
|
-
import { HOST_YUM_INSTALLABLE_PACKAGES_KEY
|
7
|
+
import { HOST_YUM_INSTALLABLE_PACKAGES_KEY } from '../PackagesTab/YumInstallablePackagesConstants';
|
8
8
|
import { REX_FEATURES } from '../RemoteExecutionConstants';
|
9
9
|
|
10
|
+
jest.mock('../../hostDetailsHelpers', () => ({
|
11
|
+
...jest.requireActual('../../hostDetailsHelpers'),
|
12
|
+
userPermissionsFromHostDetails: () => ({
|
13
|
+
create_job_invocations: true,
|
14
|
+
edit_hosts: true,
|
15
|
+
}),
|
16
|
+
}));
|
17
|
+
|
10
18
|
const contentFacetAttributes = {
|
11
19
|
id: 11,
|
12
20
|
uuid: 'e5761ea3-4117-4ecf-83d0-b694f99b389e',
|
@@ -32,7 +40,6 @@ const renderOptions = (facetAttributes = contentFacetAttributes) => ({
|
|
32
40
|
|
33
41
|
const hostYumInstallablePackages = katelloApi.getApiUrl('/packages');
|
34
42
|
const hostPackages = foremanApi.getApiUrl('/hosts/1/packages/install');
|
35
|
-
const jobInvocations = foremanApi.getApiUrl('/job_invocations');
|
36
43
|
const autocompleteUrl = '/hosts/1/packages/auto_complete_search';
|
37
44
|
const fakeTask = { id: '21c0f9e4-b27b-49aa-8774-6be66126043b' };
|
38
45
|
|
@@ -77,6 +84,7 @@ test('Can call API for installable packages and show on screen on page load', as
|
|
77
84
|
hostId={1}
|
78
85
|
hostName="test-host"
|
79
86
|
showKatelloAgent={false}
|
87
|
+
triggerPackageInstall={jest.fn()}
|
80
88
|
/>, renderOptions());
|
81
89
|
|
82
90
|
// Assert that the packages are now showing on the screen, but wait for them to appear.
|
@@ -109,6 +117,7 @@ test('Can handle no installable packages being present', async (done) => {
|
|
109
117
|
hostId={1}
|
110
118
|
hostName="test-host"
|
111
119
|
showKatelloAgent={false}
|
120
|
+
triggerPackageInstall={jest.fn()}
|
112
121
|
/>, renderOptions());
|
113
122
|
|
114
123
|
// Assert that there are not any packages showing on the screen.
|
@@ -134,6 +143,7 @@ test('Does not show katello-agent option when disabled', async (done) => {
|
|
134
143
|
hostId={1}
|
135
144
|
hostName="test-host"
|
136
145
|
showKatelloAgent={false}
|
146
|
+
triggerPackageInstall={jest.fn()}
|
137
147
|
/>, renderOptions());
|
138
148
|
|
139
149
|
// Assert that the packages are now showing on the screen, but wait for them to appear.
|
@@ -172,6 +182,7 @@ test('Shows the katello-agent option when enabled', async (done) => {
|
|
172
182
|
hostId={1}
|
173
183
|
hostName="test-host"
|
174
184
|
showKatelloAgent
|
185
|
+
triggerPackageInstall={jest.fn()}
|
175
186
|
/>, renderOptions());
|
176
187
|
|
177
188
|
// Assert that the packages are now showing on the screen, but wait for them to appear.
|
@@ -209,6 +220,7 @@ test('Can install packages via katello-agent', async (done) => {
|
|
209
220
|
hostId={1}
|
210
221
|
hostName="test-host"
|
211
222
|
showKatelloAgent
|
223
|
+
triggerPackageInstall={jest.fn()}
|
212
224
|
/>, renderOptions());
|
213
225
|
|
214
226
|
await patientlyWaitFor(() => expect(getAllByText(firstPackages.name)[0]).toBeInTheDocument());
|
@@ -236,17 +248,7 @@ test('Can install a package via remote execution', async (done) => {
|
|
236
248
|
.get(hostYumInstallablePackages)
|
237
249
|
.query(defaultQuery)
|
238
250
|
.reply(200, mockPackagesData);
|
239
|
-
const
|
240
|
-
.post(jobInvocations, {
|
241
|
-
job_invocation: {
|
242
|
-
inputs: {
|
243
|
-
[PACKAGE_SEARCH_QUERY]: `id ^ (${firstPackages.id},${secondPackages.id})`,
|
244
|
-
},
|
245
|
-
search_query: 'name ^ (test-host)',
|
246
|
-
feature: REX_FEATURES.KATELLO_PACKAGE_INSTALL_BY_SEARCH,
|
247
|
-
},
|
248
|
-
})
|
249
|
-
.reply(201);
|
251
|
+
const triggerPackageInstall = jest.fn();
|
250
252
|
|
251
253
|
const {
|
252
254
|
getAllByText, getByText, getByRole,
|
@@ -256,6 +258,7 @@ test('Can install a package via remote execution', async (done) => {
|
|
256
258
|
hostId={1}
|
257
259
|
hostName="test-host"
|
258
260
|
showKatelloAgent
|
261
|
+
triggerPackageInstall={triggerPackageInstall}
|
259
262
|
/>, renderOptions());
|
260
263
|
|
261
264
|
await patientlyWaitFor(() => expect(getAllByText(firstPackages.name)[0]).toBeInTheDocument());
|
@@ -271,9 +274,9 @@ test('Can install a package via remote execution', async (done) => {
|
|
271
274
|
const rexOption = getByText('Install via remote execution');
|
272
275
|
fireEvent.click(rexOption);
|
273
276
|
|
277
|
+
expect(triggerPackageInstall).toHaveBeenCalled();
|
274
278
|
assertNockRequest(autocompleteScope);
|
275
|
-
assertNockRequest(scope);
|
276
|
-
assertNockRequest(installScope, done);
|
279
|
+
assertNockRequest(scope, done);
|
277
280
|
});
|
278
281
|
|
279
282
|
test('Can install a package via customized remote execution', async (done) => {
|
@@ -290,6 +293,7 @@ test('Can install a package via customized remote execution', async (done) => {
|
|
290
293
|
closeModal={jest.fn()}
|
291
294
|
hostId={1}
|
292
295
|
hostName="test-host"
|
296
|
+
triggerPackageInstall={jest.fn()}
|
293
297
|
/>, renderOptions());
|
294
298
|
|
295
299
|
await patientlyWaitFor(() => expect(getAllByText(firstPackages.name)[0]).toBeInTheDocument());
|
@@ -326,6 +330,7 @@ test('Uses package_install_by_search_query template when in select all mode', as
|
|
326
330
|
closeModal={jest.fn()}
|
327
331
|
hostId={1}
|
328
332
|
hostName="test-host"
|
333
|
+
triggerPackageInstall={jest.fn()}
|
329
334
|
/>, renderOptions());
|
330
335
|
|
331
336
|
await patientlyWaitFor(() => expect(getAllByText(firstPackages.name)[0]).toBeInTheDocument());
|
@@ -364,6 +369,7 @@ test('Disables the katello-agent option when in select all mode', async (done) =
|
|
364
369
|
hostId={1}
|
365
370
|
hostName="test-host"
|
366
371
|
showKatelloAgent
|
372
|
+
triggerPackageInstall={jest.fn()}
|
367
373
|
/>, renderOptions());
|
368
374
|
|
369
375
|
await patientlyWaitFor(() => expect(getAllByText(firstPackages.name)[0]).toBeInTheDocument());
|
@@ -9,6 +9,14 @@ import { REX_FEATURES } from '../RemoteExecutionConstants';
|
|
9
9
|
import * as hooks from '../../../../Table/TableHooks';
|
10
10
|
import mockBookmarkData from './bookmarks.fixtures.json';
|
11
11
|
|
12
|
+
jest.mock('../../hostDetailsHelpers', () => ({
|
13
|
+
...jest.requireActual('../../hostDetailsHelpers'),
|
14
|
+
userPermissionsFromHostDetails: () => ({
|
15
|
+
create_job_invocations: true,
|
16
|
+
edit_hosts: true,
|
17
|
+
}),
|
18
|
+
}));
|
19
|
+
|
12
20
|
const contentFacetAttributes = {
|
13
21
|
id: 11,
|
14
22
|
uuid: 'e5761ea3-4117-4ecf-83d0-b694f99b389e',
|
@@ -41,6 +41,7 @@
|
|
41
41
|
"type": "yum",
|
42
42
|
"gpgUrl": null,
|
43
43
|
"contentUrl": "/custom/ParthaProduct/empty_repo",
|
44
|
+
"osRestricted": null,
|
44
45
|
"override": "default",
|
45
46
|
"overrides": [],
|
46
47
|
"enabled_content_override": null
|
@@ -75,6 +76,7 @@
|
|
75
76
|
"type": "yum",
|
76
77
|
"gpgUrl": null,
|
77
78
|
"contentUrl": "/custom/ParthaProduct/partha_multi-errata",
|
79
|
+
"osRestricted": "rhel-7",
|
78
80
|
"override": "0",
|
79
81
|
"overrides": [
|
80
82
|
{
|
@@ -107,6 +109,7 @@
|
|
107
109
|
"type": "yum",
|
108
110
|
"gpgUrl": null,
|
109
111
|
"contentUrl": "/custom/Pull_Provider/yggdrasil",
|
112
|
+
"osRestricted": null,
|
110
113
|
"override": "1",
|
111
114
|
"overrides": [
|
112
115
|
{
|
@@ -117,4 +120,4 @@
|
|
117
120
|
"enabled_content_override": true
|
118
121
|
}
|
119
122
|
]
|
120
|
-
}
|
123
|
+
}
|
@@ -8,6 +8,16 @@ import mockRepoSetData from './repositorySets.fixtures.json';
|
|
8
8
|
import mockBookmarkData from './bookmarks.fixtures.json';
|
9
9
|
import mockContentOverride from './contentOverrides.fixtures.json';
|
10
10
|
|
11
|
+
jest.mock('../../hostDetailsHelpers', () => ({
|
12
|
+
...jest.requireActual('../../hostDetailsHelpers'),
|
13
|
+
userPermissionsFromHostDetails: () => ({
|
14
|
+
view_hosts: true,
|
15
|
+
view_activation_keys: true,
|
16
|
+
view_products: true,
|
17
|
+
edit_hosts: true,
|
18
|
+
}),
|
19
|
+
}));
|
20
|
+
|
11
21
|
const contentFacetAttributes = {
|
12
22
|
id: 11,
|
13
23
|
uuid: 'e5761ea3-4117-4ecf-83d0-b694f99b389e',
|
@@ -378,3 +388,19 @@ test('Can filter by status', async (done) => {
|
|
378
388
|
assertNockRequest(autoSearchScope);
|
379
389
|
assertNockRequest(scope2, done); // Pass jest callback to confirm test is done
|
380
390
|
});
|
391
|
+
|
392
|
+
test('Can display osRestricted as a label', async (done) => {
|
393
|
+
const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl);
|
394
|
+
const scope = nockInstance
|
395
|
+
.get(hostRepositorySets)
|
396
|
+
.query(defaultQuery)
|
397
|
+
.reply(200, mockRepoSetData);
|
398
|
+
|
399
|
+
const { getByText } = renderWithRedux(<RepositorySetsTab />, renderOptions());
|
400
|
+
|
401
|
+
await patientlyWaitFor(() => expect(getByText(secondRepoSet.contentUrl)).toBeInTheDocument());
|
402
|
+
expect(secondRepoSet.osRestricted).not.toBeNull();
|
403
|
+
expect(getByText(secondRepoSet.osRestricted)).toBeInTheDocument();
|
404
|
+
assertNockRequest(autocompleteScope);
|
405
|
+
assertNockRequest(scope, done); // Pass jest callback to confirm test is done
|
406
|
+
});
|
@@ -14,6 +14,12 @@ import mockBookmarkData from './bookmarks.fixtures.json';
|
|
14
14
|
const hostName = 'client.example.com';
|
15
15
|
const tracesBookmarks = foremanApi.getApiUrl('/bookmarks?search=controller%3Dkatello_host_tracers');
|
16
16
|
|
17
|
+
jest.mock('../../hostDetailsHelpers', () => ({
|
18
|
+
...jest.requireActual('../../hostDetailsHelpers'),
|
19
|
+
userPermissionsFromHostDetails: () => ({
|
20
|
+
create_job_invocations: true,
|
21
|
+
}),
|
22
|
+
}));
|
17
23
|
|
18
24
|
const tracerInstalledResponse = {
|
19
25
|
id: 1,
|
@@ -117,7 +123,6 @@ describe('With tracer installed', () => {
|
|
117
123
|
const scope = nockInstance
|
118
124
|
.get(hostTraces)
|
119
125
|
.query(true)
|
120
|
-
.times(2)
|
121
126
|
.reply(200, mockTraceData);
|
122
127
|
const resolveTracesScope = nockInstance
|
123
128
|
.post(jobInvocations)
|
@@ -140,7 +145,7 @@ describe('With tracer installed', () => {
|
|
140
145
|
const restartAppButton = getByText('Restart app');
|
141
146
|
// wait 50ms so that the button is enabled
|
142
147
|
await waitFor(() => {
|
143
|
-
expect(
|
148
|
+
expect(restartAppButton.parentElement).not.toHaveClass('pf-m-disabled');
|
144
149
|
restartAppButton.click();
|
145
150
|
});
|
146
151
|
|
@@ -157,7 +162,6 @@ describe('With tracer installed', () => {
|
|
157
162
|
const scope = nockInstance
|
158
163
|
.get(hostTraces)
|
159
164
|
.query(true)
|
160
|
-
.times(2)
|
161
165
|
.reply(200, mockTraceData);
|
162
166
|
const resolveTracesScope = nockInstance
|
163
167
|
.post(jobInvocations)
|
@@ -195,7 +199,6 @@ describe('With tracer installed', () => {
|
|
195
199
|
const scope = nockInstance
|
196
200
|
.get(hostTraces)
|
197
201
|
.query(true)
|
198
|
-
.times(2)
|
199
202
|
.reply(200, mockTraceData);
|
200
203
|
const jobInvocationBody = ({ job_invocation: { inputs } }) =>
|
201
204
|
inputs[TRACES_SEARCH_QUERY] === `id !^ (${firstTrace.id},${thirdTrace.id})`;
|
@@ -25,4 +25,22 @@ export const hostIsNotRegistered = ({ hostDetails }) => {
|
|
25
25
|
|
26
26
|
export const hostIsRegistered = ({ hostDetails }) => !hostIsNotRegistered({ hostDetails });
|
27
27
|
|
28
|
+
export const userPermissionsFromHostDetails = ({ hostDetails }) => {
|
29
|
+
const {
|
30
|
+
permissions: hostPermissions,
|
31
|
+
content_facet_attributes: cfAttributes = {},
|
32
|
+
} = hostDetails;
|
33
|
+
return { ...hostPermissions, ...cfAttributes?.permissions };
|
34
|
+
};
|
35
|
+
|
36
|
+
// requiredPermissions is an array
|
37
|
+
// userPermissions is an object, e.g. { view_hosts: true }
|
38
|
+
export const hasRequiredPermissions = (requiredPermissions = [], userPermissions) => {
|
39
|
+
const permittedActions = Object.keys(userPermissions).filter(key => userPermissions[key]);
|
40
|
+
return requiredPermissions.every(permission => permittedActions.includes(permission));
|
41
|
+
};
|
42
|
+
|
43
|
+
export const missingRequiredPermissions = (requiredPermissions = [], userPermissions) =>
|
44
|
+
!hasRequiredPermissions(requiredPermissions, userPermissions);
|
45
|
+
|
28
46
|
export default defaultRemoteActionMethod;
|
data/webpack/global_index.js
CHANGED
@@ -11,7 +11,6 @@ import ErrataOverviewCard from './components/extensions/HostDetails/Cards/Errata
|
|
11
11
|
import InstalledProductsCard from './components/extensions/HostDetails/DetailsTabCards/InstalledProductsCard';
|
12
12
|
import RegistrationCard from './components/extensions/HostDetails/DetailsTabCards/RegistrationCard';
|
13
13
|
|
14
|
-
import RepositorySetsTab from './components/extensions/HostDetails/Tabs/RepositorySetsTab/RepositorySetsTab';
|
15
14
|
import TracesTab from './components/extensions/HostDetails/Tabs/TracesTab/TracesTab.js';
|
16
15
|
import extendReducer from './components/extensions/reducers';
|
17
16
|
import rootReducer from './redux/reducers';
|
@@ -19,6 +18,7 @@ import HostCollectionsCard from './components/extensions/HostDetails/Cards/HostC
|
|
19
18
|
import { hostIsNotRegistered } from './components/extensions/HostDetails/hostDetailsHelpers';
|
20
19
|
import SystemPropertiesCardExtensions from './components/extensions/HostDetails/DetailsTabCards/SystemPropertiesCardExtensions';
|
21
20
|
import HostActionsBar from './components/extensions/HostDetails/ActionsBar';
|
21
|
+
import RecentCommunicationCardExtensions from './components/extensions/HostDetails/DetailsTabCards/RecentCommunicationCardExtensions';
|
22
22
|
|
23
23
|
registerReducer('katelloExtends', extendReducer);
|
24
24
|
registerReducer('katello', rootReducer);
|
@@ -29,7 +29,6 @@ addGlobalFill('registrationAdvanced', '[katello]RegistrationCommands', <Registra
|
|
29
29
|
// Host details page tabs
|
30
30
|
addGlobalFill('host-details-page-tabs', 'Content', <ContentTab key="content" />, 900, { title: __('Content'), hideTab: hostIsNotRegistered });
|
31
31
|
addGlobalFill('host-details-page-tabs', 'Traces', <TracesTab key="traces" />, 800, { title: __('Traces'), hideTab: hostIsNotRegistered });
|
32
|
-
addGlobalFill('host-details-page-tabs', 'Repository sets', <RepositorySetsTab key="repository-sets" />, 700, { title: __('Repository sets'), hideTab: hostIsNotRegistered });
|
33
32
|
|
34
33
|
// Overview tab cards
|
35
34
|
addGlobalFill(
|
@@ -45,6 +44,7 @@ addGlobalFill(
|
|
45
44
|
700,
|
46
45
|
);
|
47
46
|
addGlobalFill('host-overview-cards', 'Installable errata', <ErrataOverviewCard key="errata-overview" />, 1900);
|
47
|
+
addGlobalFill('recent-communication-card-item', 'Recent communication', <RecentCommunicationCardExtensions key="recent-communication" />, 3000);
|
48
48
|
|
49
49
|
// Details tab cards & card extensions
|
50
50
|
addGlobalFill('host-tab-details-cards', 'Installed products', <InstalledProductsCard key="installed-products" />, 100);
|
@@ -23,9 +23,9 @@ const recommendedRepositoriesRHEL = [
|
|
23
23
|
];
|
24
24
|
|
25
25
|
const recommendedRepositoriesSatTools = [
|
26
|
+
'satellite-client-6-for-rhel-9-x86_64-rpms',
|
26
27
|
'satellite-client-6-for-rhel-8-x86_64-rpms',
|
27
28
|
'rhel-7-server-satellite-client-6-rpms',
|
28
|
-
'rhel-7-server-satellite-maintenance-6.11-rpms',
|
29
29
|
'rhel-6-server-els-satellite-client-6-rpms',
|
30
30
|
];
|
31
31
|
|
@@ -35,6 +35,10 @@ const recommendedRepositoriesMisc = [
|
|
35
35
|
'satellite-capsule-6.11-for-rhel-8-x86_64-rpms',
|
36
36
|
'rhel-7-server-ansible-2.9-rpms',
|
37
37
|
'ansible-2-for-rhel-8-x86_64-rpms',
|
38
|
+
'rhel-7-server-satellite-maintenance-6.11-rpms',
|
39
|
+
'rhel-7-server-satellite-utils-6.11-rpms',
|
40
|
+
'satellite-maintenance-6.11-for-rhel-8-x86_64-rpms',
|
41
|
+
'satellite-utils-6.11-for-rhel-8-x86_64-rpms',
|
38
42
|
];
|
39
43
|
|
40
44
|
const recommendedRepositorySetLables = recommendedRepositoriesRHEL
|
@@ -1,8 +1,9 @@
|
|
1
1
|
import { API_OPERATIONS, APIActions, get, post } from 'foremanReact/redux/API';
|
2
2
|
import { translate as __ } from 'foremanReact/common/I18n';
|
3
3
|
import api, { orgId } from '../../services/api';
|
4
|
-
import ACS_KEY, { CREATE_ACS_KEY, DELETE_ACS_KEY } from './ACSConstants';
|
4
|
+
import ACS_KEY, { acsRefreshKey, CREATE_ACS_KEY, DELETE_ACS_KEY } from './ACSConstants';
|
5
5
|
import { getResponseErrorMsgs } from '../../utils/helpers';
|
6
|
+
import { renderTaskStartedToast } from '../Tasks/helpers';
|
6
7
|
|
7
8
|
const acsSuccessToast = (response) => {
|
8
9
|
const { data: { name } } = response;
|
@@ -47,6 +48,17 @@ export const deleteACS = (acsId, handleSuccess) => APIActions.delete({
|
|
47
48
|
errorToast: error => __(`Something went wrong while deleting this alternate content source! ${getResponseErrorMsgs(error.response)}`),
|
48
49
|
});
|
49
50
|
|
51
|
+
export const refreshACS = (acsId, handleSuccess) => post({
|
52
|
+
type: API_OPERATIONS.POST,
|
53
|
+
key: acsRefreshKey(acsId),
|
54
|
+
url: api.getApiUrl(`/alternate_content_sources/${acsId}/refresh`),
|
55
|
+
params: { id: acsId },
|
56
|
+
handleSuccess: (response) => {
|
57
|
+
if (handleSuccess) handleSuccess();
|
58
|
+
return renderTaskStartedToast(response.data);
|
59
|
+
},
|
60
|
+
errorToast: error => __(`Something went wrong while refreshing this alternate content source! ${getResponseErrorMsgs(error.response)}`),
|
61
|
+
});
|
50
62
|
export default getAlternateContentSources;
|
51
63
|
|
52
64
|
// acs = Katello::AlternateContentSource.new
|
@@ -1,4 +1,18 @@
|
|
1
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
2
|
+
|
1
3
|
const ACS_KEY = 'ACS';
|
2
4
|
export const CREATE_ACS_KEY = 'ACS_CREATE';
|
3
5
|
export const DELETE_ACS_KEY = 'ACS_DELETE';
|
6
|
+
export const SMART_PROXY_KEY = 'SMART_PROXY';
|
7
|
+
export const SSL_CERTS = 'SSL_CERTS';
|
8
|
+
export const acsRefreshKey = acsId => `${ACS_KEY}_REFRESH_${acsId}`;
|
9
|
+
|
10
|
+
export const YUM = __('Yum');
|
11
|
+
export const FILE = __('File');
|
12
|
+
|
13
|
+
export const ACS_TYPE_TRANSLATIONS_ENUM = {
|
14
|
+
[YUM]: 'yum',
|
15
|
+
[FILE]: 'file',
|
16
|
+
};
|
17
|
+
|
4
18
|
export default ACS_KEY;
|
@@ -4,7 +4,7 @@ import {
|
|
4
4
|
selectAPIResponse,
|
5
5
|
} from 'foremanReact/redux/API/APISelectors';
|
6
6
|
import { STATUS } from 'foremanReact/constants';
|
7
|
-
import ACS_KEY from './ACSConstants';
|
7
|
+
import ACS_KEY, { CREATE_ACS_KEY } from './ACSConstants';
|
8
8
|
|
9
9
|
export const selectAlternateContentSources = (state, index = '') => selectAPIResponse(state, ACS_KEY + index) || {};
|
10
10
|
|
@@ -13,3 +13,12 @@ export const selectAlternateContentSourcesStatus = (state, index = '') =>
|
|
13
13
|
|
14
14
|
export const selectAlternateContentSourcesError = (state, index = '') =>
|
15
15
|
selectAPIError(state, ACS_KEY + index);
|
16
|
+
|
17
|
+
export const selectCreateACS = state =>
|
18
|
+
selectAPIResponse(state, CREATE_ACS_KEY) || {};
|
19
|
+
|
20
|
+
export const selectCreateACSStatus = state =>
|
21
|
+
selectAPIStatus(state, CREATE_ACS_KEY) || STATUS.PENDING;
|
22
|
+
|
23
|
+
export const selectCreateACSError = state =>
|
24
|
+
selectAPIError(state, CREATE_ACS_KEY);
|
@@ -0,0 +1,160 @@
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
2
|
+
import { useDispatch } from 'react-redux';
|
3
|
+
import PropTypes from 'prop-types';
|
4
|
+
import { Wizard } from '@patternfly/react-core';
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
6
|
+
import ACSCreateContext from './ACSCreateContext';
|
7
|
+
import SelectSource from './Steps/SelectSource';
|
8
|
+
import NameACS from './Steps/NameACS';
|
9
|
+
import AcsUrlPaths from './Steps/AcsUrlPaths';
|
10
|
+
import ACSCredentials from './Steps/ACSCredentials';
|
11
|
+
import ACSSmartProxies from './Steps/ACSSmartProxies';
|
12
|
+
import ACSReview from './Steps/ACSReview';
|
13
|
+
import ACSCreateFinish from './Steps/ACSCreateFinish';
|
14
|
+
import { getContentCredentials } from '../../ContentCredentials/ContentCredentialActions';
|
15
|
+
import { getSmartProxies } from '../../SmartProxy/SmartProxyContentActions';
|
16
|
+
import { CONTENT_CREDENTIAL_CERT_TYPE } from '../../ContentCredentials/ContentCredentialConstants';
|
17
|
+
|
18
|
+
const ACSCreateWizard = ({ show, setIsOpen }) => {
|
19
|
+
const [acsType, setAcsType] = useState(null);
|
20
|
+
const [contentType, setContentType] = useState('yum');
|
21
|
+
const [name, setName] = useState('');
|
22
|
+
const [description, setDescription] = useState('');
|
23
|
+
const [smartProxies, setSmartProxies] = useState([]);
|
24
|
+
const [url, setUrl] = useState('');
|
25
|
+
const [subpaths, setSubpaths] = useState('');
|
26
|
+
const [verifySSL, setVerifySSL] = useState(false);
|
27
|
+
const [authentication, setAuthentication] = useState('');
|
28
|
+
const [sslCert, setSslCert] = useState('');
|
29
|
+
const [sslKey, setSslKey] = useState('');
|
30
|
+
const [sslCertName, setSslCertName] = useState('');
|
31
|
+
const [sslKeyName, setSslKeyName] = useState('');
|
32
|
+
const [username, setUsername] = useState('');
|
33
|
+
const [password, setPassword] = useState('');
|
34
|
+
const [caCert, setCACert] = useState('');
|
35
|
+
const [caCertName, setCACertName] = useState('');
|
36
|
+
const [currentStep, setCurrentStep] = useState(1);
|
37
|
+
const dispatch = useDispatch();
|
38
|
+
|
39
|
+
useEffect(
|
40
|
+
() => {
|
41
|
+
dispatch(getContentCredentials({ content_type: CONTENT_CREDENTIAL_CERT_TYPE }));
|
42
|
+
dispatch(getSmartProxies());
|
43
|
+
},
|
44
|
+
[dispatch],
|
45
|
+
);
|
46
|
+
|
47
|
+
const steps = [
|
48
|
+
{
|
49
|
+
id: 1,
|
50
|
+
name: __('Select source type'),
|
51
|
+
component: <SelectSource />,
|
52
|
+
enableNext: acsType && contentType,
|
53
|
+
},
|
54
|
+
{
|
55
|
+
id: 2,
|
56
|
+
name: __('Name source'),
|
57
|
+
component: <NameACS />,
|
58
|
+
enableNext: name !== '',
|
59
|
+
},
|
60
|
+
{
|
61
|
+
id: 3,
|
62
|
+
name: __('Select smart proxy'),
|
63
|
+
component: <ACSSmartProxies />,
|
64
|
+
enableNext: smartProxies.length,
|
65
|
+
},
|
66
|
+
{
|
67
|
+
id: 4,
|
68
|
+
name: __('URL and paths'),
|
69
|
+
component: <AcsUrlPaths />,
|
70
|
+
enableNext: url !== '' && subpaths !== '',
|
71
|
+
},
|
72
|
+
{
|
73
|
+
id: 5,
|
74
|
+
name: __('Credentials'),
|
75
|
+
component: <ACSCredentials />,
|
76
|
+
},
|
77
|
+
{
|
78
|
+
id: 6,
|
79
|
+
name: __('Review details'),
|
80
|
+
component: <ACSReview />,
|
81
|
+
nextButtonText: __('Add'),
|
82
|
+
},
|
83
|
+
{
|
84
|
+
id: 7,
|
85
|
+
name: __('Create ACS'),
|
86
|
+
component: <ACSCreateFinish />,
|
87
|
+
isFinishedStep: true,
|
88
|
+
},
|
89
|
+
|
90
|
+
];
|
91
|
+
|
92
|
+
return (
|
93
|
+
<ACSCreateContext.Provider value={{
|
94
|
+
show,
|
95
|
+
setIsOpen,
|
96
|
+
currentStep,
|
97
|
+
setCurrentStep,
|
98
|
+
acsType,
|
99
|
+
setAcsType,
|
100
|
+
contentType,
|
101
|
+
setContentType,
|
102
|
+
name,
|
103
|
+
setName,
|
104
|
+
description,
|
105
|
+
setDescription,
|
106
|
+
smartProxies,
|
107
|
+
setSmartProxies,
|
108
|
+
url,
|
109
|
+
setUrl,
|
110
|
+
subpaths,
|
111
|
+
setSubpaths,
|
112
|
+
verifySSL,
|
113
|
+
setVerifySSL,
|
114
|
+
authentication,
|
115
|
+
setAuthentication,
|
116
|
+
sslCert,
|
117
|
+
setSslCert,
|
118
|
+
sslKey,
|
119
|
+
setSslKey,
|
120
|
+
sslCertName,
|
121
|
+
setSslCertName,
|
122
|
+
sslKeyName,
|
123
|
+
setSslKeyName,
|
124
|
+
username,
|
125
|
+
setUsername,
|
126
|
+
password,
|
127
|
+
setPassword,
|
128
|
+
caCert,
|
129
|
+
setCACert,
|
130
|
+
caCertName,
|
131
|
+
setCACertName,
|
132
|
+
}}
|
133
|
+
>
|
134
|
+
<Wizard
|
135
|
+
title={__('Add an alternate content source')}
|
136
|
+
steps={steps}
|
137
|
+
startAtStep={currentStep}
|
138
|
+
onGoToStep={({ id }) => setCurrentStep(id)}
|
139
|
+
onNext={({ id }) => setCurrentStep(id)}
|
140
|
+
onBack={({ id }) => setCurrentStep(id)}
|
141
|
+
onClose={() => {
|
142
|
+
setIsOpen(false);
|
143
|
+
}}
|
144
|
+
isOpen={show}
|
145
|
+
/>
|
146
|
+
</ACSCreateContext.Provider>
|
147
|
+
);
|
148
|
+
};
|
149
|
+
|
150
|
+
ACSCreateWizard.propTypes = {
|
151
|
+
show: PropTypes.bool,
|
152
|
+
setIsOpen: PropTypes.func,
|
153
|
+
};
|
154
|
+
|
155
|
+
ACSCreateWizard.defaultProps = {
|
156
|
+
show: false,
|
157
|
+
setIsOpen: null,
|
158
|
+
};
|
159
|
+
|
160
|
+
export default ACSCreateWizard;
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import React, { useContext, useState } from 'react';
|
2
|
+
import { useDispatch, useSelector } from 'react-redux';
|
3
|
+
import useDeepCompareEffect from 'use-deep-compare-effect';
|
4
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
5
|
+
import { STATUS } from 'foremanReact/constants';
|
6
|
+
import ACSCreateContext from '../ACSCreateContext';
|
7
|
+
import { selectCreateACS, selectCreateACSError, selectCreateACSStatus } from '../../ACSSelectors';
|
8
|
+
import getAlternateContentSources, { createACS } from '../../ACSActions';
|
9
|
+
import Loading from '../../../../components/Loading';
|
10
|
+
|
11
|
+
const ACSCreateFinish = () => {
|
12
|
+
const {
|
13
|
+
currentStep,
|
14
|
+
setIsOpen,
|
15
|
+
acsType,
|
16
|
+
contentType,
|
17
|
+
name,
|
18
|
+
description,
|
19
|
+
smartProxies,
|
20
|
+
url,
|
21
|
+
subpaths,
|
22
|
+
verifySSL,
|
23
|
+
authentication,
|
24
|
+
sslCert,
|
25
|
+
sslKey,
|
26
|
+
username,
|
27
|
+
password,
|
28
|
+
caCert,
|
29
|
+
} = useContext(ACSCreateContext);
|
30
|
+
const dispatch = useDispatch();
|
31
|
+
const response = useSelector(state => selectCreateACS(state));
|
32
|
+
const status = useSelector(state => selectCreateACSStatus(state));
|
33
|
+
const error = useSelector(state => selectCreateACSError(state));
|
34
|
+
const [createACSDispatched, setCreateACSDispatched] = useState(false);
|
35
|
+
const [saving, setSaving] = useState(true);
|
36
|
+
|
37
|
+
useDeepCompareEffect(() => {
|
38
|
+
if (currentStep === 7 && !createACSDispatched) {
|
39
|
+
setCreateACSDispatched(true);
|
40
|
+
let params = {
|
41
|
+
name,
|
42
|
+
description,
|
43
|
+
base_url: url,
|
44
|
+
subpaths: subpaths.split(','),
|
45
|
+
smart_proxy_names: smartProxies,
|
46
|
+
content_type: contentType,
|
47
|
+
alternate_content_source_type: acsType,
|
48
|
+
verify_ssl: verifySSL,
|
49
|
+
ssl_ca_cert_id: caCert,
|
50
|
+
};
|
51
|
+
if (authentication === 'content_credentials') {
|
52
|
+
params = { ssl_client_cert_id: sslCert, ssl_client_key_id: sslKey, ...params };
|
53
|
+
}
|
54
|
+
if (authentication === 'manual') {
|
55
|
+
params = { upstream_username: username, upstream_password: password, ...params };
|
56
|
+
}
|
57
|
+
dispatch(createACS(params));
|
58
|
+
}
|
59
|
+
}, [dispatch, createACSDispatched, setCreateACSDispatched,
|
60
|
+
acsType, authentication, name, description, url, subpaths,
|
61
|
+
smartProxies, contentType, verifySSL, caCert, sslCert, sslKey,
|
62
|
+
username, password, currentStep]);
|
63
|
+
|
64
|
+
useDeepCompareEffect(() => {
|
65
|
+
const { id } = response;
|
66
|
+
if (id && status === STATUS.RESOLVED && saving) {
|
67
|
+
setSaving(false);
|
68
|
+
dispatch(getAlternateContentSources());
|
69
|
+
setIsOpen(false);
|
70
|
+
} else if (status === STATUS.ERROR) {
|
71
|
+
setSaving(false);
|
72
|
+
setIsOpen(false);
|
73
|
+
}
|
74
|
+
}, [response, status, error, saving, dispatch, setIsOpen]);
|
75
|
+
|
76
|
+
return <Loading loadingText={__('Saving alternate content source...')} />;
|
77
|
+
};
|
78
|
+
|
79
|
+
export default ACSCreateFinish;
|