foreman_rh_cloud 3.0.18.1 → 3.0.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/foreman_inventory_upload/tasks_controller.rb +14 -3
  3. data/app/controllers/foreman_inventory_upload/uploads_settings_controller.rb +8 -0
  4. data/app/models/insights_resolution.rb +1 -1
  5. data/config/routes.rb +1 -1
  6. data/lib/foreman_inventory_upload.rb +5 -0
  7. data/lib/foreman_inventory_upload/generators/queries.rb +1 -1
  8. data/lib/foreman_inventory_upload/scripts/uploader.sh.erb +5 -1
  9. data/lib/foreman_rh_cloud/version.rb +1 -1
  10. data/lib/insights_cloud/async/insights_full_sync.rb +17 -21
  11. data/lib/inventory_sync/async/host_result.rb +11 -6
  12. data/lib/inventory_sync/async/inventory_full_sync.rb +24 -41
  13. data/lib/inventory_sync/async/inventory_hosts_sync.rb +34 -0
  14. data/lib/inventory_sync/async/query_inventory_job.rb +54 -0
  15. data/lib/tasks/insights.rake +1 -1
  16. data/package.json +1 -1
  17. data/test/jobs/insights_full_sync_test.rb +14 -10
  18. data/test/jobs/inventory_full_sync_test.rb +181 -11
  19. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/InventoryFilter.js +1 -1
  20. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/integration.test.js.snap +0 -1
  21. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/SyncButtonActions.js +81 -46
  22. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/SyncButtonConstants.js +3 -3
  23. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/SyncButtonSelectors.js +6 -12
  24. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/SyncButtonFixtures.js +1 -9
  25. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/SyncButtonSelectors.test.js +18 -27
  26. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/SyncButtonSelectors.test.js.snap +1 -16
  27. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/integrations.test.js.snap +58 -0
  28. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/integrations.test.js +51 -0
  29. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/index.js +2 -5
  30. data/webpack/ForemanInventoryUpload/ForemanInventoryUploadReducers.js +0 -2
  31. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTable.test.js.snap +2 -0
  32. data/webpack/__mocks__/foremanReact/redux/middlewares/IntervalMiddleware.js +4 -0
  33. metadata +25 -25
  34. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/SyncButtonReducer.js +0 -36
  35. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/SyncButtonActions.test.js +0 -31
  36. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/SyncButtonReducer.test.js +0 -26
  37. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/SyncButtonActions.test.js.snap +0 -98
  38. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/SyncButtonReducer.test.js.snap +0 -18
@@ -2,13 +2,25 @@ require 'test_plugin_helper'
2
2
 
3
3
  class InventoryFullSyncTest < ActiveJob::TestCase
4
4
  setup do
5
- @host1 = FactoryBot.create(:host, :managed, name: 'host1')
6
-
7
5
  User.current = User.find_by(login: 'secret_admin')
8
6
 
9
7
  env = FactoryBot.create(:katello_k_t_environment)
10
8
  cv = env.content_views << FactoryBot.create(:katello_content_view, organization: env.organization)
11
9
 
10
+ # this host would pass our plugin queries, so it could be uploaded to the cloud.
11
+ @host1 = FactoryBot.create(
12
+ :host,
13
+ :with_subscription,
14
+ :with_content,
15
+ content_view: cv.first,
16
+ lifecycle_environment: env,
17
+ organization: env.organization
18
+ )
19
+
20
+ pool = FactoryBot.create(:katello_pool, account_number: '1234', cp_id: 1)
21
+
22
+ @host1.subscription_facet.pools << pool
23
+
12
24
  # this host would pass our plugin queries, so it could be uploaded to the cloud.
13
25
  @host2 = FactoryBot.create(
14
26
  :host,
@@ -19,7 +31,8 @@ class InventoryFullSyncTest < ActiveJob::TestCase
19
31
  organization: env.organization
20
32
  )
21
33
 
22
- @host2.subscription_facet.pools << FactoryBot.create(:katello_pool, account_number: '1234', cp_id: 1)
34
+ @host2.subscription_facet.pools << pool
35
+ @host2_inventory_id = '4536bf5c-ff03-4154-a8c9-32ff4b40e40c'
23
36
 
24
37
  ForemanInventoryUpload::Generators::Queries.instance_variable_set(:@fact_names, nil)
25
38
 
@@ -28,8 +41,165 @@ class InventoryFullSyncTest < ActiveJob::TestCase
28
41
  "total": 3,
29
42
  "count": 3,
30
43
  "page": 1,
31
- "per_page": 50,
32
- "results": [{"fqdn": "#{@host1.fqdn}"}]
44
+ "per_page": 3,
45
+ "results": [
46
+ {
47
+ "insights_id": "72d29d75-dbbf-4121-9566-2f581ab77f36",
48
+ "rhel_machine_id": null,
49
+ "subscription_manager_id": "#{@host2.subscription_facet.uuid}",
50
+ "satellite_id": "bb72bf95-0a19-4090-8009-f0d8c68aca61",
51
+ "bios_uuid": "b48a7e5f-cb50-4029-a75e-366bf43db641",
52
+ "ip_addresses": [
53
+ "192.168.122.56"
54
+ ],
55
+ "fqdn": "#{@host2.fqdn}",
56
+ "mac_addresses": [
57
+ "52:54:00:aa:12:12",
58
+ "00:00:00:00:00:00"
59
+ ],
60
+ "external_id": null,
61
+ "id": "#{@host2_inventory_id}",
62
+ "account": "1460290",
63
+ "display_name": "insights-rh7.example.com",
64
+ "ansible_host": null,
65
+ "facts": [
66
+ {
67
+ "namespace": "satellite",
68
+ "facts": {
69
+ "virtual_host_name": "virt-who-nobody.home-1",
70
+ "satellite_instance_id": "fc4d0cb0-a0b0-421e-b096-b028319b8e47",
71
+ "is_simple_content_access": false,
72
+ "distribution_version": "7.3",
73
+ "satellite_version": "6.8.4",
74
+ "organization_id": 1,
75
+ "is_hostname_obfuscated": false,
76
+ "virtual_host_uuid": "a90e6294-4766-420a-8dc0-3ec5b96d60ec"
77
+ }
78
+ },
79
+ {
80
+ "namespace": "yupana",
81
+ "facts": {
82
+ "report_platform_id": "d37afa50-08ce-4efb-a0e5-759c2a016661",
83
+ "report_slice_id": "5bf791d7-5e30-4a3c-929a-11dd9fa6eb72",
84
+ "source": "Satellite",
85
+ "yupana_host_id": "e85958b6-58db-4cfd-aeb6-01ee81bc0f43",
86
+ "account": "1460290"
87
+ }
88
+ }
89
+ ],
90
+ "reporter": "puptoo",
91
+ "stale_timestamp": "2021-03-19T07:57:42.466399+00:00",
92
+ "stale_warning_timestamp": "2021-03-26T07:57:42.466399+00:00",
93
+ "culled_timestamp": "2021-04-02T07:57:42.466399+00:00",
94
+ "created": "2021-02-08T14:36:03.613880+00:00",
95
+ "updated": "2021-03-18T02:57:42.535250+00:00"
96
+ },
97
+ {
98
+ "insights_id": "e0dc5144-d78e-43ed-97be-a7a21d1b6946",
99
+ "rhel_machine_id": null,
100
+ "subscription_manager_id": "0f97ee19-6862-4900-aea4-f121c8754776",
101
+ "satellite_id": "0f97ee19-6862-4900-aea4-f121c8754776",
102
+ "bios_uuid": "6a0b199a-8225-4ade-ae44-3b18cfc84a01",
103
+ "ip_addresses": [
104
+ "192.168.122.136"
105
+ ],
106
+ "fqdn": "#{@host1.fqdn}",
107
+ "mac_addresses": [
108
+ "52:54:00:02:d1:2a",
109
+ "00:00:00:00:00:00"
110
+ ],
111
+ "external_id": null,
112
+ "id": "3255d080-e6c1-44e2-8d72-b044b9a38d8f",
113
+ "account": "1460290",
114
+ "display_name": "insights-rh8.example.com",
115
+ "ansible_host": null,
116
+ "facts": [
117
+ {
118
+ "namespace": "satellite",
119
+ "facts": {
120
+ "virtual_host_name": "virt-who-nobody.home-1",
121
+ "satellite_instance_id": "fc4d0cb0-a0b0-421e-b096-b028319b8e47",
122
+ "is_simple_content_access": false,
123
+ "distribution_version": "8.3",
124
+ "satellite_version": "6.8.4",
125
+ "organization_id": 1,
126
+ "is_hostname_obfuscated": false,
127
+ "virtual_host_uuid": "a90e6294-4766-420a-8dc0-3ec5b96d60ec"
128
+ }
129
+ },
130
+ {
131
+ "namespace": "yupana",
132
+ "facts": {
133
+ "report_platform_id": "d37afa50-08ce-4efb-a0e5-759c2a016661",
134
+ "report_slice_id": "5bf791d7-5e30-4a3c-929a-11dd9fa6eb72",
135
+ "source": "Satellite",
136
+ "yupana_host_id": "78c62486-0ac4-406c-a4c0-3a1f81112a2d",
137
+ "account": "1460290"
138
+ }
139
+ }
140
+ ],
141
+ "reporter": "puptoo",
142
+ "stale_timestamp": "2021-03-19T06:05:12.092136+00:00",
143
+ "stale_warning_timestamp": "2021-03-26T06:05:12.092136+00:00",
144
+ "culled_timestamp": "2021-04-02T06:05:12.092136+00:00",
145
+ "created": "2021-02-08T13:22:50.555671+00:00",
146
+ "updated": "2021-03-18T01:05:12.131847+00:00"
147
+ },
148
+ {
149
+ "insights_id": "b533848e-465f-4f1a-9b2b-b71cb2d5239d",
150
+ "rhel_machine_id": null,
151
+ "subscription_manager_id": "d29bde40-348e-437c-8acf-8fa98320fc1b",
152
+ "satellite_id": "d29bde40-348e-437c-8acf-8fa98320fc1b",
153
+ "bios_uuid": "3cd5d972-cfb5-451a-8314-fd2f56629d7c",
154
+ "ip_addresses": [
155
+ "172.16.5.39",
156
+ "fd6e:2298:736e::857",
157
+ "fd6e:2298:736e:0:2c66:6101:9cc6:2b23"
158
+ ],
159
+ "fqdn": "rhel8-demo.oss-lab.net",
160
+ "mac_addresses": [
161
+ "6e:66:a6:fe:fc:07",
162
+ "00:00:00:00:00:00"
163
+ ],
164
+ "external_id": null,
165
+ "id": "59ab38db-c25b-4fc7-bfb2-c8eb01d865a9",
166
+ "account": "1460290",
167
+ "display_name": "rhel8-demo.oss-lab.net",
168
+ "ansible_host": null,
169
+ "facts": [
170
+ {
171
+ "namespace": "satellite",
172
+ "facts": {
173
+ "satellite_instance_id": "fb3643d8-9030-487b-b95c-684783806ffd",
174
+ "system_purpose_sla": "",
175
+ "is_simple_content_access": false,
176
+ "distribution_version": "8.3",
177
+ "satellite_version": "6.8.1",
178
+ "organization_id": 1,
179
+ "system_purpose_role": "",
180
+ "system_purpose_usage": "",
181
+ "is_hostname_obfuscated": false
182
+ }
183
+ },
184
+ {
185
+ "namespace": "yupana",
186
+ "facts": {
187
+ "report_platform_id": "fa8b924c-51ee-479d-97d2-b4623cf1d4aa",
188
+ "report_slice_id": "0b49103f-6471-4ade-ad74-a51537bc5691",
189
+ "source": "Satellite",
190
+ "yupana_host_id": "30e43340-12fb-445d-b23f-faaf5cbc2092",
191
+ "account": "1460290"
192
+ }
193
+ }
194
+ ],
195
+ "reporter": "puptoo",
196
+ "stale_timestamp": "2021-03-19T05:55:23.700781+00:00",
197
+ "stale_warning_timestamp": "2021-03-26T05:55:23.700781+00:00",
198
+ "culled_timestamp": "2021-04-02T05:55:23.700781+00:00",
199
+ "created": "2021-01-13T20:05:51.059012+00:00",
200
+ "updated": "2021-03-18T00:55:23.739162+00:00"
201
+ }
202
+ ]
33
203
  }
34
204
  INVENTORY_JSON
35
205
  @inventory = JSON.parse(inventory_json)
@@ -71,21 +241,21 @@ class InventoryFullSyncTest < ActiveJob::TestCase
71
241
  test 'Host status should be SYNC for inventory hosts' do
72
242
  InventorySync::Async::InventoryFullSync.any_instance.expects(:query_inventory).returns(@inventory)
73
243
 
74
- InventorySync::Async::InventoryFullSync.perform_now(@host1.organization)
244
+ ForemanTasks.sync_task(InventorySync::Async::InventoryFullSync, @host2.organization)
75
245
 
76
- @host1.reload
246
+ @host2.reload
77
247
 
78
- assert_equal InventorySync::InventoryStatus::SYNC, InventorySync::InventoryStatus.where(host_id: @host1.id).first.status
248
+ assert_equal InventorySync::InventoryStatus::SYNC, InventorySync::InventoryStatus.where(host_id: @host2.id).first.status
249
+ assert_equal @host2_inventory_id, @host2.insights.uuid
79
250
  end
80
251
 
81
252
  test 'Host status should be DISCONNECT for hosts that are not returned from cloud' do
82
253
  InventorySync::Async::InventoryFullSync.any_instance.expects(:query_inventory).returns(@inventory)
83
254
  FactoryBot.create(:fact_value, fact_name: fact_names['virt::uuid'], value: '1234', host: @host2)
84
255
 
85
- InventorySync::Async::InventoryFullSync.perform_now(@host2.organization)
86
-
256
+ ForemanTasks.sync_task(InventorySync::Async::InventoryFullSync, @host1.organization)
87
257
  @host2.reload
88
258
 
89
- assert_equal InventorySync::InventoryStatus::DISCONNECT, InventorySync::InventoryStatus.where(host_id: @host2.id).first.status
259
+ assert_equal InventorySync::InventoryStatus::DISCONNECT, InventorySync::InventoryStatus.where(host_id: @host1.id).first.status
90
260
  end
91
261
  end
@@ -18,7 +18,7 @@ const InventoryFilter = ({
18
18
  const initialTerm =
19
19
  organization === __(ANY_ORGANIZATION) ? '' : organization;
20
20
  handleFilterChange(initialTerm);
21
- }, [organization]);
21
+ }, []);
22
22
 
23
23
  return (
24
24
  <form id="inventory_filter_form">
@@ -37,7 +37,6 @@ Object {
37
37
  "inventoryFilter": Object {
38
38
  "filterTerm": "some-org",
39
39
  },
40
- "inventorySync": Object {},
41
40
  },
42
41
  },
43
42
  },
@@ -1,57 +1,92 @@
1
1
  import React from 'react';
2
- import { API } from 'foremanReact/redux/API';
2
+ import { get, post } from 'foremanReact/redux/API';
3
+ import { withInterval } from 'foremanReact/redux/middlewares/IntervalMiddleware';
3
4
  import { addToast } from 'foremanReact/redux/actions/toasts';
5
+ import { translate as __ } from 'foremanReact/common/I18n';
4
6
  import { inventoryUrl } from '../../../../ForemanInventoryHelpers';
5
7
  import Toast from './components/Toast';
6
8
  import {
7
- INVENTORY_SYNC_REQUEST,
8
- INVENTORY_SYNC_SUCCESS,
9
- INVENTORY_SYNC_ERROR,
9
+ INVENTORY_SYNC,
10
+ INVENTORY_SYNC_TASK_UPDATE,
10
11
  } from './SyncButtonConstants';
12
+ import { foremanUrl } from '../../../../../ForemanRhCloudHelpers';
11
13
 
12
- export const handleSync = () => async dispatch => {
13
- dispatch({
14
- type: INVENTORY_SYNC_REQUEST,
15
- payload: {},
16
- });
17
- try {
18
- const {
19
- data: { syncHosts, disconnectHosts },
20
- } = await API.post(inventoryUrl('tasks'));
21
- dispatch({
22
- type: INVENTORY_SYNC_SUCCESS,
23
- payload: {
24
- syncHosts,
25
- disconnectHosts,
26
- },
27
- });
28
-
29
- dispatch(
30
- addToast({
31
- sticky: true,
32
- type: 'success',
33
- message: (
34
- <Toast syncHosts={syncHosts} disconnectHosts={disconnectHosts} />
35
- ),
36
- })
37
- );
38
- } catch ({
39
- message,
40
- response: { data: { message: toastMessage } = {} } = {},
41
- }) {
42
- dispatch({
43
- type: INVENTORY_SYNC_ERROR,
44
- payload: {
45
- error: message,
14
+ export const handleSync = () => dispatch => {
15
+ dispatch(
16
+ post({
17
+ key: INVENTORY_SYNC,
18
+ url: inventoryUrl('tasks'),
19
+ handleSuccess: ({
20
+ data: {
21
+ task: { id },
22
+ },
23
+ }) => {
24
+ dispatch(getSyncTaskInterval(id));
25
+ return dispatch(
26
+ taskPageRefererToast(id, 'info', __('Inventory sync has started:'))
27
+ );
46
28
  },
47
- });
29
+ errorToast: inventorySyncErrorToast,
30
+ })
31
+ );
32
+ };
48
33
 
49
- dispatch(
50
- addToast({
51
- sticky: true,
52
- type: 'error',
53
- message: toastMessage || message,
34
+ export const getSyncTaskInterval = id => dispatch => {
35
+ dispatch(
36
+ withInterval(
37
+ get({
38
+ key: INVENTORY_SYNC_TASK_UPDATE,
39
+ url: inventoryUrl(`tasks/${id}`),
40
+ handleSuccess: ({ data: { result, output } }, stopTaskInterval) => {
41
+ if (result === 'success') {
42
+ const {
43
+ host_statuses: { sync, disconnect },
44
+ } = output;
45
+ dispatch(
46
+ addToast({
47
+ sticky: true,
48
+ type: 'success',
49
+ message: (
50
+ <Toast syncHosts={sync} disconnectHosts={disconnect} />
51
+ ),
52
+ })
53
+ );
54
+ }
55
+ if (result === 'error') {
56
+ dispatch(
57
+ taskPageRefererToast(
58
+ id,
59
+ 'error',
60
+ __('Inventory sync has failed:'),
61
+ true
62
+ )
63
+ );
64
+ }
65
+ stopTaskInterval();
66
+ },
67
+ errorToast: inventorySyncErrorToast,
54
68
  })
55
- );
56
- }
69
+ )
70
+ );
57
71
  };
72
+
73
+ const inventorySyncErrorToast = ({ message, response }) =>
74
+ `${__('Inventory sync has failed: ')} ${response.data?.message || message}`;
75
+
76
+ const taskPageRefererToast = (taskID, toastType, prefix, sticky = false) =>
77
+ addToast({
78
+ sticky,
79
+ type: toastType,
80
+ message: (
81
+ <span>
82
+ {prefix}{' '}
83
+ <a
84
+ target="_blank"
85
+ rel="noopener noreferrer"
86
+ href={foremanUrl(`/foreman_tasks/tasks/${taskID}`)}
87
+ >
88
+ {__('view the task page for more details')}
89
+ </a>
90
+ </span>
91
+ ),
92
+ });
@@ -1,3 +1,3 @@
1
- export const INVENTORY_SYNC_REQUEST = 'INVENTORY_SYNC_REQUEST';
2
- export const INVENTORY_SYNC_SUCCESS = 'INVENTORY_SYNC_SUCCESS';
3
- export const INVENTORY_SYNC_ERROR = 'INVENTORY_SYNC_ERROR';
1
+ export const INVENTORY_SYNC = 'INVENTORY_SYNC';
2
+
3
+ export const INVENTORY_SYNC_TASK_UPDATE = 'INVENTORY_SYNC_TASK_UPDATE';
@@ -1,13 +1,7 @@
1
- import { selectForemanInventoryUpload } from '../../../../../ForemanRhCloudSelectors';
1
+ import { selectAPIResponse } from 'foremanReact/redux/API/APISelectors';
2
+ import { INVENTORY_SYNC_TASK_UPDATE } from './SyncButtonConstants';
2
3
 
3
- export const selectInventorySync = state =>
4
- selectForemanInventoryUpload(state).inventorySync;
5
-
6
- export const selectStatus = state => selectInventorySync(state).status;
7
-
8
- export const selectError = state => selectInventorySync(state).error;
9
-
10
- export const selectSyncHosts = state => selectInventorySync(state).syncHosts;
11
-
12
- export const selectDisconnectHosts = state =>
13
- selectInventorySync(state).disconnectHosts;
4
+ export const selectTaskStatus = state => {
5
+ const { result } = selectAPIResponse(state, INVENTORY_SYNC_TASK_UPDATE);
6
+ return typeof result === 'string' ? result.toUpperCase() : null;
7
+ };
@@ -1,9 +1 @@
1
- export const syncHosts = 1;
2
-
3
- export const disconnectHosts = 0;
4
-
5
- export const successResponse = { data: { syncHosts, disconnectHosts } };
6
-
7
- export const status = 'RESOLVED';
8
-
9
- export const error = 'some-error';
1
+ export const successResponse = { data: { task: { id: 1 } } };
@@ -1,35 +1,26 @@
1
1
  import { testSelectorsSnapshotWithFixtures } from '@theforeman/test';
2
- import { inventoryStateWrapper } from '../../../../../../ForemanRhCloudTestHelpers';
3
- import {
4
- status,
5
- error,
6
- syncHosts,
7
- disconnectHosts,
8
- } from './SyncButtonFixtures';
9
- import {
10
- selectInventorySync,
11
- selectStatus,
12
- selectError,
13
- selectSyncHosts,
14
- selectDisconnectHosts,
15
- } from '../SyncButtonSelectors';
2
+ import { selectTaskStatus } from '../SyncButtonSelectors';
16
3
 
17
- const state = inventoryStateWrapper({
18
- inventorySync: {
19
- status,
20
- error,
21
- syncHosts,
22
- disconnectHosts,
4
+ const state = {
5
+ API: {
6
+ INVENTORY_SYNC_TASK_UPDATE: {
7
+ response: {
8
+ endedAt: '2021-03-08T14:27:30.718+02:00',
9
+ output: {
10
+ host_statuses: {
11
+ sync: 0,
12
+ disconnect: 2,
13
+ },
14
+ },
15
+ result: 'pending',
16
+ },
17
+ status: 'RESOLVED',
18
+ },
23
19
  },
24
- });
20
+ };
25
21
 
26
22
  const fixtures = {
27
- 'should return InventorySync': () => selectInventorySync(state),
28
- 'should return InventorySync status': () => selectStatus(state),
29
- 'should return InventorySync error': () => selectError(state),
30
- 'should return InventorySync SyncHosts': () => selectSyncHosts(state),
31
- 'should return InventorySync disconnectHosts': () =>
32
- selectDisconnectHosts(state),
23
+ 'should return InventorySync status': () => selectTaskStatus(state),
33
24
  };
34
25
 
35
26
  describe('SyncButton selectors', () =>