foreman_inventory_upload 1.0.0.beta7 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/foreman_inventory_upload/accounts_controller.rb +39 -0
  3. data/app/controllers/foreman_inventory_upload/reports_controller.rb +3 -4
  4. data/app/controllers/foreman_inventory_upload/uploads_controller.rb +3 -3
  5. data/app/views/scripts/uploader.sh.erb +21 -9
  6. data/config/routes.rb +5 -5
  7. data/lib/foreman_inventory_upload.rb +14 -10
  8. data/lib/foreman_inventory_upload/async/generate_all_reports_job.rb +4 -7
  9. data/lib/foreman_inventory_upload/async/generate_report_job.rb +13 -9
  10. data/lib/foreman_inventory_upload/async/queue_for_upload_job.rb +7 -7
  11. data/lib/foreman_inventory_upload/async/upload_report_job.rb +22 -17
  12. data/lib/foreman_inventory_upload/generators/archived_report.rb +2 -2
  13. data/lib/foreman_inventory_upload/generators/queries.rb +18 -9
  14. data/lib/foreman_inventory_upload/generators/slice.rb +16 -9
  15. data/lib/foreman_inventory_upload/version.rb +1 -1
  16. data/lib/tasks/generator.rake +19 -5
  17. data/test/controllers/{statuses_controller_test.rb → accounts_controller_test.rb} +6 -7
  18. data/test/controllers/reports_controller_test.rb +3 -3
  19. data/test/controllers/uploads_controller_test.rb +3 -3
  20. data/test/unit/archived_report_generator_test.rb +3 -3
  21. data/test/unit/slice_generator_test.rb +22 -0
  22. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.fixtures.js +5 -2
  23. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.js +11 -9
  24. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountListActions.js +3 -3
  25. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountListReducer.js +7 -7
  26. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountListSelectors.js +1 -1
  27. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.fixtures.js +1 -1
  28. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.js +9 -7
  29. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/__snapshots__/ListItem.test.js.snap +6 -4
  30. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/ListItemStatus.fixtures.js +2 -1
  31. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/ListItemStatus.js +5 -5
  32. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListReducer.test.js +2 -2
  33. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListSelectors.test.js +4 -4
  34. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountList.test.js.snap +15 -12
  35. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListReducer.test.js.snap +13 -10
  36. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListSelectors.test.js.snap +9 -3
  37. data/webpack/ForemanInventoryUpload/Components/AccountList/index.js +3 -6
  38. data/webpack/ForemanInventoryUpload/Components/Dashboard/Dashboard.js +5 -5
  39. data/webpack/ForemanInventoryUpload/Components/Dashboard/index.js +1 -4
  40. data/webpack/ForemanInventoryUpload/Components/NavContainer/__tests__/__snapshots__/NavContainer.test.js.snap +4 -4
  41. data/webpack/ForemanInventoryUpload/Components/StatusChart/StatusChart.js +4 -1
  42. metadata +7 -7
  43. data/app/controllers/foreman_inventory_upload/statuses_controller.rb +0 -41
@@ -1,3 +1,3 @@
1
1
  module ForemanInventoryUpload
2
- VERSION = '1.0.0.beta7'.freeze
2
+ VERSION = '1.0.0'.freeze
3
3
  end
@@ -4,12 +4,26 @@ namespace :foreman_inventory_upload do
4
4
  namespace :report do
5
5
  desc 'Generate inventory report to be sent to Red Hat cloud'
6
6
  task generate: :environment do
7
- target = ENV['target'] || ForemanInventoryUpload.facts_archive_name
8
- portal_user = ENV['portal_user'] || 'anonymous'
7
+ portal_user = ENV['portal_user']
8
+ organizations = [ENV['organization_id']]
9
+ base_folder = ENV['target'] || Dir.pwd
9
10
 
10
- archived_report_generator = ForemanInventoryUpload::Generators::ArchivedReport.new(target, Logger.new(STDOUT))
11
- archived_report_generator.render(portal_user)
12
- puts "Successfully generated #{target} for #{portal_user}"
11
+ unless portal_user || organizations.empty?
12
+ puts "Must specify either portal_user or organization_id"
13
+ end
14
+
15
+ if portal_user
16
+ puts "Generating report for all organizations associated with #{portal_user}"
17
+ base_folder = File.join(base_folder, portal_user)
18
+ organizations = ForemanInventoryUpload::Generators::Queries.organizations_for_user(portal_user).pluck(:id)
19
+ end
20
+
21
+ organizations.each do |organization|
22
+ target = File.join(base_folder, ForemanInventoryUpload.facts_archive_name(organization))
23
+ archived_report_generator = ForemanInventoryUpload::Generators::ArchivedReport.new(target, Logger.new(STDOUT))
24
+ archived_report_generator.render(organization: organization)
25
+ puts "Successfully generated #{target} for organization id #{organization}"
26
+ end
13
27
  end
14
28
  end
15
29
  end
@@ -1,25 +1,24 @@
1
1
  require 'test_plugin_helper'
2
2
 
3
- class StatusesControllerTest < ActionController::TestCase
4
- tests ForemanInventoryUpload::StatusesController
3
+ class AccountsControllerTest < ActionController::TestCase
4
+ tests ForemanInventoryUpload::AccountsController
5
5
 
6
6
  include FolderIsolation
7
7
 
8
8
  test 'Returns statuses for each process type' do
9
- configuration = RedhatAccess::TelemetryConfiguration.new(enable_telemetry: true, portal_user: 'test')
10
- configuration.save!
9
+ test_org = FactoryBot.create(:organization)
11
10
 
12
- generate_label = ForemanInventoryUpload::Async::GenerateReportJob.output_label('test')
11
+ generate_label = ForemanInventoryUpload::Async::GenerateReportJob.output_label(test_org.id)
13
12
  generate_output = ForemanInventoryUpload::Async::ProgressOutput.register(generate_label)
14
13
  generate_output.status = 'generate_status_test'
15
- upload_label = ForemanInventoryUpload::Async::UploadReportJob.output_label('test')
14
+ upload_label = ForemanInventoryUpload::Async::UploadReportJob.output_label(test_org.id)
16
15
  upload_output = ForemanInventoryUpload::Async::ProgressOutput.register(upload_label)
17
16
  upload_output.status = 'upload_status_test'
18
17
 
19
18
  get :index, session: set_session_user
20
19
 
21
20
  assert_response :success
22
- actual = JSON.parse(response.body)['statuses']['test']
21
+ actual = JSON.parse(response.body)['accounts'][test_org.id.to_s]
23
22
  assert_equal 'generate_status_test', actual['generate_report_status']
24
23
  assert_equal 'upload_status_test', actual['upload_report_status']
25
24
  end
@@ -5,14 +5,14 @@ class ReportsControllerTest < ActionController::TestCase
5
5
 
6
6
  test 'Returns latest report generation status' do
7
7
  progress_output = mock('progress_output')
8
- test_portal_user = 'test_portal_user'
8
+ test_org = FactoryBot.create(:organization)
9
9
  ForemanInventoryUpload::Async::ProgressOutput
10
10
  .expects(:get)
11
- .with(ForemanInventoryUpload::Async::GenerateReportJob.output_label(test_portal_user))
11
+ .with(ForemanInventoryUpload::Async::GenerateReportJob.output_label(test_org.id))
12
12
  .returns(progress_output)
13
13
  progress_output.expects(:full_output).returns('test output')
14
14
 
15
- get :last, params: { portal_user: test_portal_user }, session: set_session_user
15
+ get :last, params: { organization_id: test_org.id }, session: set_session_user
16
16
 
17
17
  assert_response :success
18
18
  actual = JSON.parse(response.body)
@@ -5,14 +5,14 @@ class UploadsControllerTest < ActionController::TestCase
5
5
 
6
6
  test 'Returns latest upload status' do
7
7
  progress_output = mock('progress_output')
8
- test_portal_user = 'test_portal_user'
8
+ test_org = FactoryBot.create(:organization)
9
9
  ForemanInventoryUpload::Async::ProgressOutput
10
10
  .expects(:get)
11
- .with(ForemanInventoryUpload::Async::UploadReportJob.output_label(test_portal_user))
11
+ .with(ForemanInventoryUpload::Async::UploadReportJob.output_label(test_org.id))
12
12
  .returns(progress_output)
13
13
  progress_output.expects(:full_output).returns('test output')
14
14
 
15
- get :last, params: { portal_user: test_portal_user }, session: set_session_user
15
+ get :last, params: { organization_id: test_org.id }, session: set_session_user
16
16
 
17
17
  assert_response :success
18
18
  actual = JSON.parse(response.body)
@@ -46,13 +46,13 @@ class ArchivedReportGeneratorTest < ActiveSupport::TestCase
46
46
 
47
47
  test 'generates a report for a single host' do
48
48
  batches = Host.where(id: @host.id).in_batches
49
- portal_user = 'test_portal_user'
49
+ test_org = FactoryBot.create(:organization)
50
50
 
51
- ForemanInventoryUpload::Generators::Queries.expects(:for_report).with(portal_user).returns(batches)
51
+ ForemanInventoryUpload::Generators::Queries.expects(:for_org).with(test_org.id).returns(batches)
52
52
  Dir.mktmpdir do |tmpdir|
53
53
  target = File.join(tmpdir, 'test.tar.gz')
54
54
  generator = ForemanInventoryUpload::Generators::ArchivedReport.new(target, Logger.new(STDOUT))
55
- generator.render(portal_user)
55
+ generator.render(organization: test_org.id)
56
56
 
57
57
  files = Dir["#{tmpdir}/*"]
58
58
  assert_equal "#{tmpdir}/test.tar.gz", files.first
@@ -109,4 +109,26 @@ class ReportGeneratorTest < ActiveSupport::TestCase
109
109
  assert_equal hypervisor_host.name, fact_values['virtual_host_name']
110
110
  assert_equal hypervisor_host.subscription_facet.uuid, fact_values['virtual_host_uuid']
111
111
  end
112
+
113
+ test 'generates a report with system purpose' do
114
+ @host.subscription_facet.purpose_usage = 'test_usage'
115
+ @host.subscription_facet.purpose_role = 'test_role'
116
+ @host.subscription_facet.save!
117
+
118
+ batch = Host.where(id: @host.id).in_batches.first
119
+ generator = ForemanInventoryUpload::Generators::Slice.new(batch, [], 'slice_123')
120
+
121
+ json_str = generator.render
122
+ actual = JSON.parse(json_str.join("\n"))
123
+
124
+ assert_equal 'slice_123', actual['report_slice_id']
125
+ assert_not_nil(actual_host = actual['hosts'].first)
126
+ assert_equal @host.name, actual_host['display_name']
127
+ assert_equal @host.fqdn, actual_host['fqdn']
128
+ assert_not_nil(host_facts = actual_host['facts']&.first)
129
+ assert_equal 'satellite', host_facts['namespace']
130
+ assert_not_nil(fact_values = host_facts['facts'])
131
+ assert_equal 'test_usage', fact_values['system_purpose_usage']
132
+ assert_equal 'test_role', fact_values['system_purpose_role']
133
+ end
112
134
  end
@@ -2,20 +2,23 @@ import { noop } from 'patternfly-react';
2
2
 
3
3
  export const API_SUCCESS_RESPONSE = {
4
4
  Account1: {
5
+ label: 'test_org1',
5
6
  upload_report_status: 'running',
6
7
  generate_report_status: 'running',
7
8
  },
8
9
  Account2: {
10
+ label: 'test_org2',
9
11
  upload_report_status: 'unknown',
10
12
  generate_report_status: 'failure',
11
13
  },
12
14
  Account3: {
15
+ label: 'test_org3',
13
16
  upload_report_status: 'success',
14
17
  generate_report_status: 'running',
15
18
  },
16
19
  };
17
20
 
18
- export const statuses = API_SUCCESS_RESPONSE;
21
+ export const accounts = API_SUCCESS_RESPONSE;
19
22
 
20
23
  export const pollingProcessID = 0;
21
24
 
@@ -26,7 +29,7 @@ export const accountID = 'user@redhat.com';
26
29
  export const processStatusName = 'upload_report_status';
27
30
 
28
31
  export const props = {
29
- statuses,
32
+ accounts,
30
33
  fetchAccountsStatus: noop,
31
34
  startAccountStatusPolling: noop,
32
35
  stopAccountStatusPolling: noop,
@@ -20,23 +20,23 @@ class AccountList extends Component {
20
20
  }
21
21
 
22
22
  render() {
23
- const { statuses, error } = this.props;
24
- const accountNames = Object.keys(statuses);
23
+ const { accounts, error } = this.props;
24
+ const accountIds = Object.keys(accounts);
25
25
 
26
26
  if (error) {
27
27
  return <ErrorState error={error} />;
28
28
  }
29
29
 
30
- if (accountNames.length === 0) {
30
+ if (accountIds.length === 0) {
31
31
  return <EmptyState />;
32
32
  }
33
- const items = accountNames.map((name, index) => {
34
- const status = statuses[name];
33
+ const items = accountIds.map((accountID, index) => {
34
+ const account = accounts[accountID];
35
35
  return (
36
36
  <ListItem
37
37
  key={index}
38
- name={name}
39
- statuses={status}
38
+ accountID={accountID}
39
+ account={account}
40
40
  initExpanded={index === 0}
41
41
  />
42
42
  );
@@ -50,10 +50,11 @@ AccountList.propTypes = {
50
50
  startAccountStatusPolling: PropTypes.func,
51
51
  stopAccountStatusPolling: PropTypes.func,
52
52
  pollingProcessID: PropTypes.number,
53
- statuses: PropTypes.shape({
53
+ account: PropTypes.shape({
54
54
  generate_report_status: PropTypes.string,
55
55
  upload_report_status: PropTypes.string,
56
56
  }),
57
+ accounts: PropTypes.object,
57
58
  error: PropTypes.string,
58
59
  };
59
60
 
@@ -62,10 +63,11 @@ AccountList.defaultProps = {
62
63
  startAccountStatusPolling: noop,
63
64
  stopAccountStatusPolling: noop,
64
65
  pollingProcessID: 0,
65
- statuses: {
66
+ account: {
66
67
  generate_report_status: 'unknown',
67
68
  upload_report_status: 'unknown',
68
69
  },
70
+ accounts: {},
69
71
  error: '',
70
72
  };
71
73
 
@@ -10,12 +10,12 @@ import {
10
10
  export const fetchAccountsStatus = () => async dispatch => {
11
11
  try {
12
12
  const {
13
- data: { statuses },
14
- } = await API.get('statuses');
13
+ data: { accounts },
14
+ } = await API.get('accounts');
15
15
  dispatch({
16
16
  type: INVENTORY_ACCOUNT_STATUS_POLLING,
17
17
  payload: {
18
- statuses,
18
+ accounts,
19
19
  },
20
20
  });
21
21
  } catch (error) {
@@ -7,7 +7,7 @@ import {
7
7
  } from './AccountListConstants';
8
8
 
9
9
  const initialState = Immutable({
10
- statuses: {},
10
+ accounts: {},
11
11
  pollingProcessID: 0,
12
12
  error: null,
13
13
  });
@@ -17,7 +17,7 @@ export default (state = initialState, action) => {
17
17
  payload: {
18
18
  pollingProcessID,
19
19
  error,
20
- statuses,
20
+ accounts,
21
21
  accountID,
22
22
  processStatusName,
23
23
  } = {},
@@ -27,13 +27,13 @@ export default (state = initialState, action) => {
27
27
  case INVENTORY_ACCOUNT_STATUS_POLLING:
28
28
  return state.merge({
29
29
  ...state,
30
- statuses,
30
+ accounts,
31
31
  error: null,
32
32
  });
33
33
  case INVENTORY_ACCOUNT_STATUS_POLLING_ERROR:
34
34
  return state.merge({
35
35
  ...state,
36
- statuses: {},
36
+ accounts: {},
37
37
  error,
38
38
  });
39
39
  case INVENTORY_ACCOUNT_STATUS_POLLING_START:
@@ -42,10 +42,10 @@ export default (state = initialState, action) => {
42
42
  pollingProcessID,
43
43
  });
44
44
  case INVENTORY_PROCESS_RESTART:
45
- return state.setIn(['statuses'], {
46
- ...state.statuses,
45
+ return state.setIn(['accounts'], {
46
+ ...state.accounts,
47
47
  [accountID]: {
48
- ...state.statuses[accountID],
48
+ ...state.accounts[accountID],
49
49
  [processStatusName]: 'Restarting...',
50
50
  },
51
51
  });
@@ -2,7 +2,7 @@ import { selectForemanInventoryUpload } from '../../ForemanInventoryUploadSelect
2
2
 
3
3
  export const selectAccountsList = state =>
4
4
  selectForemanInventoryUpload(state).accountsList;
5
- export const selectStatuses = state => selectAccountsList(state).statuses;
5
+ export const selectAccounts = state => selectAccountsList(state).accounts;
6
6
  export const selectPollingProcessID = state =>
7
7
  selectAccountsList(state).pollingProcessID;
8
8
  export const selectError = state => selectAccountsList(state).error;
@@ -1 +1 @@
1
- export const props = { name: 'some-name' };
1
+ export const props = { accountID: '1' };
@@ -4,34 +4,36 @@ import PropTypes from 'prop-types';
4
4
  import ListItemStatus from '../ListItemStatus';
5
5
  import Dashboard from '../../../Dashboard';
6
6
 
7
- const ListItem = ({ name, statuses, initExpanded }) => (
7
+ const ListItem = ({ accountID, account, initExpanded }) => (
8
8
  <ListView.Item
9
9
  leftContent={<ListView.Icon name="user" />}
10
- heading={name}
10
+ heading={account.label}
11
11
  additionalInfo={[
12
- <ListItemStatus key={`${name}_status`} statuses={statuses} />,
12
+ <ListItemStatus key={`${accountID}_status`} account={account} />,
13
13
  ]}
14
14
  stacked
15
15
  hideCloseIcon
16
16
  initExpanded={initExpanded}
17
17
  >
18
- <Dashboard accountID={name} statuses={statuses} />
18
+ <Dashboard accountID={accountID} account={account} />
19
19
  </ListView.Item>
20
20
  );
21
21
 
22
22
  ListItem.propTypes = {
23
- name: PropTypes.string.isRequired,
24
- statuses: PropTypes.shape({
23
+ accountID: PropTypes.string.isRequired,
24
+ account: PropTypes.shape({
25
25
  generate_report_status: PropTypes.string,
26
26
  upload_report_status: PropTypes.string,
27
+ label: PropTypes.string,
27
28
  }),
28
29
  initExpanded: PropTypes.bool,
29
30
  };
30
31
 
31
32
  ListItem.defaultProps = {
32
- statuses: {
33
+ account: {
33
34
  generate_report_status: 'unknown',
34
35
  upload_report_status: 'unknown',
36
+ label: 'default_org_name',
35
37
  },
36
38
  initExpanded: false,
37
39
  };
@@ -6,9 +6,10 @@ exports[`ListItem rendering render with Props 1`] = `
6
6
  additionalInfo={
7
7
  Array [
8
8
  <ListItemStatus
9
- statuses={
9
+ account={
10
10
  Object {
11
11
  "generate_report_status": "unknown",
12
+ "label": "default_org_name",
12
13
  "upload_report_status": "unknown",
13
14
  }
14
15
  }
@@ -19,7 +20,7 @@ exports[`ListItem rendering render with Props 1`] = `
19
20
  compoundExpand={false}
20
21
  compoundExpanded={false}
21
22
  description={null}
22
- heading="some-name"
23
+ heading="default_org_name"
23
24
  hideCloseIcon={true}
24
25
  initExpanded={false}
25
26
  leftContent={
@@ -36,13 +37,14 @@ exports[`ListItem rendering render with Props 1`] = `
36
37
  stacked={true}
37
38
  >
38
39
  <Connect(Dashboard)
39
- accountID="some-name"
40
- statuses={
40
+ account={
41
41
  Object {
42
42
  "generate_report_status": "unknown",
43
+ "label": "default_org_name",
43
44
  "upload_report_status": "unknown",
44
45
  }
45
46
  }
47
+ accountID="1"
46
48
  />
47
49
  </ListViewItem>
48
50
  `;
@@ -1,6 +1,7 @@
1
1
  export const props = {
2
- statuses: {
2
+ account: {
3
3
  generate_report_status: 'Exit Code: pid 22191 exit 0',
4
4
  upload_report_status: 'Exit Code: Running in pid 22376',
5
+ label: 'Default Org',
5
6
  },
6
7
  };
@@ -5,12 +5,12 @@ import { translate as __ } from 'foremanReact/common/I18n';
5
5
  import './listItemStatus.scss';
6
6
  import { getStatusIconByRegex } from './ListItemStatusHelper';
7
7
 
8
- const ListItemStatus = ({ statuses }) => {
8
+ const ListItemStatus = ({ account }) => {
9
9
  const generatingStatusIcon = getStatusIconByRegex(
10
- statuses.generate_report_status
10
+ account.generate_report_status
11
11
  );
12
12
  const uploadingStatusIcon = getStatusIconByRegex(
13
- statuses.upload_report_status
13
+ account.upload_report_status
14
14
  );
15
15
  return (
16
16
  <Grid className="status">
@@ -27,14 +27,14 @@ const ListItemStatus = ({ statuses }) => {
27
27
  };
28
28
 
29
29
  ListItemStatus.propTypes = {
30
- statuses: PropTypes.shape({
30
+ account: PropTypes.shape({
31
31
  generate_report_status: PropTypes.string,
32
32
  upload_report_status: PropTypes.string,
33
33
  }),
34
34
  };
35
35
 
36
36
  ListItemStatus.defaultProps = {
37
- statuses: {
37
+ account: {
38
38
  generate_report_status: 'unknown',
39
39
  uploupload_report_statusading: 'unknown',
40
40
  },