foreman_rh_cloud 1.0.10 → 1.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -0
  3. data/app/controllers/insights_cloud/hits_controller.rb +17 -0
  4. data/app/models/concerns/rh_cloud_host.rb +12 -0
  5. data/app/views/hosts/_insights_tab.html.erb +15 -0
  6. data/config/routes.rb +1 -0
  7. data/lib/foreman_inventory_upload/async/generate_report_job.rb +1 -1
  8. data/lib/foreman_inventory_upload/async/shell_process.rb +15 -9
  9. data/lib/foreman_inventory_upload/async/upload_report_job.rb +19 -1
  10. data/lib/foreman_inventory_upload/generators/metadata.rb +3 -0
  11. data/lib/foreman_inventory_upload/generators/queries.rb +2 -4
  12. data/lib/foreman_rh_cloud/engine.rb +10 -0
  13. data/lib/foreman_rh_cloud/version.rb +1 -1
  14. data/lib/tasks/insights.rake +15 -0
  15. data/lib/tasks/{generator.rake → rh_cloud_inventory.rake} +7 -1
  16. data/package.json +3 -1
  17. data/test/jobs/upload_report_job_test.rb +34 -0
  18. data/test/unit/metadata_generator_test.rb +2 -0
  19. data/test/unit/slice_generator_test.rb +17 -0
  20. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/integration.test.js.snap +3 -0
  21. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/components/Toast.js +1 -1
  22. data/webpack/ForemanInventoryUpload/Components/StatusChart/StatusChart.js +1 -1
  23. data/webpack/ForemanInventoryUpload/Components/StatusChart/__tests__/__snapshots__/StatusChart.test.js.snap +1 -1
  24. data/webpack/ForemanInventoryUpload/Components/TabContainer/tabContainer.scss +1 -1
  25. data/webpack/ForemanRhCloudPages.js +2 -0
  26. data/webpack/ForemanRhCloudReducers.js +2 -0
  27. data/webpack/ForemanRhCloudSelectors.js +5 -0
  28. data/webpack/ForemanRhCloudTestHelpers.js +6 -1
  29. data/webpack/InsightsHostDetailsTab/InsightsTab.js +64 -0
  30. data/webpack/InsightsHostDetailsTab/InsightsTab.scss +86 -0
  31. data/webpack/InsightsHostDetailsTab/InsightsTabActions.js +30 -0
  32. data/webpack/InsightsHostDetailsTab/InsightsTabConstants.js +3 -0
  33. data/webpack/InsightsHostDetailsTab/InsightsTabReducer.js +26 -0
  34. data/webpack/InsightsHostDetailsTab/InsightsTabSelectors.js +3 -0
  35. data/webpack/InsightsHostDetailsTab/__tests__/InsightsTab.fixtures.js +25 -0
  36. data/webpack/InsightsHostDetailsTab/__tests__/InsightsTab.test.js +13 -0
  37. data/webpack/InsightsHostDetailsTab/__tests__/InsightsTabActions.test.js +13 -0
  38. data/webpack/InsightsHostDetailsTab/__tests__/InsightsTabIntegration.test.js +17 -0
  39. data/webpack/InsightsHostDetailsTab/__tests__/InsightsTabReducer.test.js +35 -0
  40. data/webpack/InsightsHostDetailsTab/__tests__/InsightsTabSelectors.test.js +13 -0
  41. data/webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTab.test.js.snap +30 -0
  42. data/webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabActions.test.js.snap +20 -0
  43. data/webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabReducer.test.js.snap +41 -0
  44. data/webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabSelectors.test.js.snap +20 -0
  45. data/webpack/InsightsHostDetailsTab/components/ListItem/ListItem.js +69 -0
  46. data/webpack/InsightsHostDetailsTab/components/ListItem/index.js +1 -0
  47. data/webpack/InsightsHostDetailsTab/index.js +20 -0
  48. data/webpack/__tests__/__snapshots__/ForemanRhCloudSelectors.test.js.snap +1 -0
  49. data/webpack/__tests__/__snapshots__/ForemanRhCloudTestHelpers.test.js.snap +3 -0
  50. data/webpack/stories/decorators/withCardsDecorator.js +1 -1
  51. metadata +38 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb457584e95614e52c7026cb963409b2a2ea645b082315c0004b787db6ecf028
4
- data.tar.gz: 6352df86d12f35dbdc4d90538c1604aa00e1c4710f09c6deb0066efce8a48277
3
+ metadata.gz: f2b89191efb4cb97cec7c9e2a2d1cc41e255f02f7c04e95ecf7c1753e3b718ed
4
+ data.tar.gz: 38846a43180f47c4ebf888b8af0624a3edf460a10b0e3f02eca17bdd074f5142
5
5
  SHA512:
6
- metadata.gz: 1299b5836e5bf30151c690e5c222c67d34f351b0e5ffbd4e3ffeead0109457adce46279a1ddb550f3468aac4439c1050e366bf831dfb3c84b3e021e5cc2d9dd3
7
- data.tar.gz: edc7f560cd4ed663a07535bc247b0d5baf7e5755a50f002e9c752116cda93b729a15cc7b4452c12a95d9cee542d7201f900142f408abfa43a8e308d390305aa2
6
+ metadata.gz: 1d1fd157774d8b4d2007ea0139b05c6d39a749d432f6e1bcdb3d5efa5f39480743e1220434037131ccf65486edcd1fde24de39ed165653aa074062131d957394
7
+ data.tar.gz: 531cc12e64d7a4f8957029f7d7517dd80d30758c9ba070dd2603c5e64f324d399484824cc160dca8bef53810fa44aaeaf77e92e6a2d6ffdeb1f634b316a2af6e
data/README.md CHANGED
@@ -11,6 +11,39 @@ for how to install Foreman plugins
11
11
 
12
12
  *Usage here*
13
13
 
14
+ ### In Satellite
15
+
16
+ #### Inventory upload
17
+
18
+ In UI: Configure -> Inventory Upload -> Restart
19
+
20
+ From command-line:
21
+
22
+ export organization_id=1
23
+ export target=/var/lib/foreman/red_hat_inventory/generated_reports/
24
+ /usr/sbin/foreman-rake rh_cloud_inventory:report:generate
25
+
26
+ #### Fetch hosts remediation data
27
+
28
+ In UI: Configure -> Insights -> Sync now
29
+
30
+ From command-line:
31
+
32
+ /usr/sbin/foreman-rake rh_cloud_inventory:sync
33
+
34
+ #### Synchronize inventory status
35
+
36
+ In UI: Configure -> Inventory Upload -> Sync inventory status
37
+
38
+ From command-line:
39
+
40
+ # all organizations
41
+ /usr/sbin/foreman-rake rh_cloud_insights:sync
42
+
43
+ # specific organization with id 1
44
+ export organization_id=1
45
+ /usr/sbin/foreman-rake rh_cloud_insights:sync
46
+
14
47
  ## TODO
15
48
 
16
49
  *Todo list here*
@@ -0,0 +1,17 @@
1
+ module InsightsCloud
2
+ class HitsController < ::ApplicationController
3
+ def index
4
+ host = Host.where(id: host_id_param).first
5
+
6
+ render json: {
7
+ hits: host.insights.hits,
8
+ }, status: :ok
9
+ end
10
+
11
+ private
12
+
13
+ def host_id_param
14
+ params.require(:host_id)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ module RhCloudHost
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ has_many(
6
+ :inventory_upload_facts,
7
+ -> { where(fact_name_id: ForemanInventoryUpload::Generators::Queries.fact_names.values) },
8
+ class_name: 'FactValue',
9
+ :foreign_key => :host_id
10
+ )
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ <% content_for(:javascripts) do %>
2
+ <%= webpacked_plugins_js_for :'foreman_rh_cloud' %>
3
+ <% end %>
4
+
5
+ <% content_for(:stylesheets) do %>
6
+ <%= webpacked_plugins_css_for :'foreman_rh_cloud' %>
7
+ <% end %>
8
+
9
+ <div id="host_details_insights_react_container"></div>
10
+ <%= mount_react_component(
11
+ 'InsightsHostDetailsTab',
12
+ '#host_details_insights_react_container',
13
+ { hostID: host.id }.to_json,
14
+ flatten_data: true
15
+ ) %>
@@ -15,6 +15,7 @@ Rails.application.routes.draw do
15
15
  namespace :insights_cloud do
16
16
  resources :tasks, only: [:create]
17
17
  resource :settings, only: [:show, :update]
18
+ get 'hits/:host_id', to: 'hits#index'
18
19
  end
19
20
 
20
21
  namespace :foreman_rh_cloud do
@@ -23,7 +23,7 @@ module ForemanInventoryUpload
23
23
  end
24
24
 
25
25
  def command
26
- "#{rake_prefix}rake foreman_inventory_upload:report:generate"
26
+ "#{rake_prefix}rake rh_cloud_inventory:report:generate"
27
27
  end
28
28
 
29
29
  def env
@@ -8,24 +8,30 @@ module ForemanInventoryUpload
8
8
  def perform(instance_label)
9
9
  klass_name = self.class.name
10
10
  logger.debug("Starting #{klass_name} with label #{instance_label}")
11
- progress_output = ProgressOutput.register(instance_label)
12
- Open3.popen2e(hash_to_s(env), command) do |_stdin, stdout_stderr, wait_thread|
13
- progress_output.status = "Running in pid #{wait_thread.pid}"
11
+ progress_output_for(instance_label) do |progress_output|
12
+ Open3.popen2e(hash_to_s(env), command) do |_stdin, stdout_stderr, wait_thread|
13
+ progress_output.status = "Running in pid #{wait_thread.pid}"
14
14
 
15
- stdout_stderr.each do |out_line|
16
- progress_output.write_line(out_line)
17
- end
15
+ stdout_stderr.each do |out_line|
16
+ progress_output.write_line(out_line)
17
+ end
18
18
 
19
- progress_output.status = wait_thread.value.to_s
19
+ progress_output.status = wait_thread.value.to_s
20
+ end
20
21
  end
21
22
  logger.debug("Finished job #{klass_name} with label #{instance_label}")
22
- ensure
23
- progress_output.close
24
23
  end
25
24
 
26
25
  def command
27
26
  end
28
27
 
28
+ def progress_output_for(instance_label)
29
+ progress_output = ProgressOutput.register(instance_label)
30
+ yield(progress_output)
31
+ ensure
32
+ progress_output.close
33
+ end
34
+
29
35
  def env
30
36
  {}
31
37
  end
@@ -8,15 +8,33 @@ module ForemanInventoryUpload
8
8
  end
9
9
 
10
10
  def perform(filename, organization_id)
11
+ label = UploadReportJob.output_label(organization_id)
11
12
  @filename = filename
12
13
  @organization = Organization.find(organization_id)
13
14
 
15
+ if Setting[:content_disconnected]
16
+ progress_output_for(label) do |progress_output|
17
+ progress_output.write_line('Upload was stopped since disconnected mode setting is enabled for content on this instance.')
18
+ progress_output.status = "Task aborted, exit 1"
19
+ end
20
+ return
21
+ end
22
+
23
+ unless @organization.owner_details&.fetch('upstreamConsumer')&.fetch('idCert')
24
+ logger.info("Skipping organization '#{@organization}', no candlepin certificate defined.")
25
+ progress_output_for(label) do |progress_output|
26
+ progress_output.write_line("Skipping organization #{@organization}, no candlepin certificate defined.")
27
+ progress_output.status = "Task aborted, exit 1"
28
+ end
29
+ return
30
+ end
31
+
14
32
  Tempfile.create([@organization.name, '.pem']) do |cer_file|
15
33
  cer_file.write(rh_credentials[:cert])
16
34
  cer_file.write(rh_credentials[:key])
17
35
  cer_file.flush
18
36
  @cer_path = cer_file.path
19
- super(UploadReportJob.output_label(organization_id))
37
+ super(label)
20
38
  end
21
39
  end
22
40
 
@@ -21,6 +21,9 @@ module ForemanInventoryUpload
21
21
  private
22
22
 
23
23
  def render_report(metadata)
24
+ metadata ||= {}
25
+ metadata['foreman_rh_cloud_version'] = ForemanRhCloud::VERSION
26
+
24
27
  @stream.object do
25
28
  @stream.simple_field('report_id', Foreman.uuid)
26
29
  @stream.simple_field('host_inventory_api_version', '1.0')
@@ -30,23 +30,21 @@ module ForemanInventoryUpload
30
30
  end
31
31
 
32
32
  def self.for_slice(base)
33
- fact_values = FactValue.where(fact_name_id: fact_names.values)
34
33
  base
35
34
  .joins(:subscription_facet)
36
- .eager_load(:fact_values)
37
35
  .preload(
38
36
  :interfaces,
39
37
  :installed_packages,
40
38
  :content_facet,
41
39
  :host_statuses,
40
+ :inventory_upload_facts,
42
41
  subscription_facet: [:pools, :installed_products, :hypervisor_host]
43
42
  )
44
- .merge(fact_values)
45
43
  end
46
44
 
47
45
  def self.for_report(portal_user)
48
46
  org_ids = organizations_for_user(portal_user).pluck(:id)
49
- for_slice(Host.unscoped.where(organization_id: org_ids)).in_batches(of: 1_000)
47
+ for_org(org_ids)
50
48
  end
51
49
 
52
50
  def self.for_org(organization_id)
@@ -66,9 +66,19 @@ module ForemanRhCloud
66
66
  register_global_js_file 'subscriptions_extension'
67
67
 
68
68
  register_custom_status(InventorySync::InventoryStatus)
69
+
70
+ extend_page 'hosts/show' do |context|
71
+ context.add_pagelet :main_tabs,
72
+ partial: 'hosts/insights_tab',
73
+ name: _('Insights'),
74
+ onlyif: proc { |host| host.insights }
75
+ end
69
76
  end
70
77
 
71
78
  ::Katello::UINotifications::Subscriptions::ManifestImportSuccess.include ForemanInventoryUpload::Notifications::ManifestImportSuccessNotificationOverride if defined?(Katello)
79
+
80
+ ::Host::Managed.include RhCloudHost
81
+ ::Host::Base.include RhCloudHost
72
82
  end
73
83
 
74
84
  initializer "foreman_rh_cloud.set_dynflow.config.on_init", :before => :finisher_hook do |_app|
@@ -1,3 +1,3 @@
1
1
  module ForemanRhCloud
2
- VERSION = '1.0.10'.freeze
2
+ VERSION = '1.0.11'.freeze
3
3
  end
@@ -0,0 +1,15 @@
1
+ namespace :rh_cloud_insights do
2
+ desc "Synchronize Insights inventory"
3
+ task sync: :environment do
4
+ if ! ENV['organization_id'].nil?
5
+ organizations = [ Organization.where(:id => ENV['organization_id']).first ]
6
+ else
7
+ organizations = Organization.all
8
+ end
9
+
10
+ organizations.each do |organization|
11
+ InventorySync::Async::InventoryFullSync.perform_now(organization)
12
+ puts "Synchronized inventory for organization '#{organization.name}'"
13
+ end
14
+ end
15
+ end
@@ -1,6 +1,6 @@
1
1
  require 'tempfile'
2
2
 
3
- namespace :foreman_inventory_upload do
3
+ namespace :rh_cloud_inventory 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
@@ -26,4 +26,10 @@ namespace :foreman_inventory_upload do
26
26
  end
27
27
  end
28
28
  end
29
+
30
+ desc "Synchronize Insights hosts hits"
31
+ task sync: :environment do
32
+ InsightsCloud::Async::InsightsFullSync.perform_now()
33
+ puts "Synchronized Insights hosts hits data"
34
+ end
29
35
  end
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foreman_rh_cloud",
3
- "version": "0.0.1",
3
+ "version": "1.0.11",
4
4
  "description": "Inventory Upload =============",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -47,6 +47,8 @@
47
47
  "eslint": "^4.10.0",
48
48
  "eslint-import-resolver-babel-module": "^4.0.0",
49
49
  "eslint-plugin-patternfly-react": "0.2.0",
50
+ "eslint-plugin-promise": "^4.2.1",
51
+ "eslint-plugin-spellcheck": "^0.0.17",
50
52
  "identity-obj-proxy": "^3.0.0",
51
53
  "jed": "^1.1.1",
52
54
  "jest-cli": "^23.6.0",
@@ -0,0 +1,34 @@
1
+ require 'test_plugin_helper'
2
+
3
+ class UploadReportJobTest < ActiveJob::TestCase
4
+ include FolderIsolation
5
+
6
+ test 'returns aborted state when disconnected' do
7
+ organization = FactoryBot.create(:organization)
8
+ Organization.any_instance.stubs(:owner_details).returns(
9
+ 'upstreamConsumer' => {
10
+ 'idCert' => 'TEST_CERT',
11
+ }
12
+ )
13
+ FactoryBot.create(:setting, :name => 'content_disconnected', :value => true)
14
+
15
+ ForemanInventoryUpload::Async::UploadReportJob.perform_now('', organization.id)
16
+
17
+ label = ForemanInventoryUpload::Async::UploadReportJob.output_label(organization.id)
18
+ progress_output = ForemanInventoryUpload::Async::ProgressOutput.get(label)
19
+ assert_match(/Upload was stopped/, progress_output.full_output)
20
+ assert_match(/exit 1/, progress_output.status)
21
+ end
22
+
23
+ test 'returns aborted state when no certificate defined on organization' do
24
+ organization = FactoryBot.create(:organization)
25
+ Organization.any_instance.expects(:owner_details).returns(nil)
26
+
27
+ ForemanInventoryUpload::Async::UploadReportJob.perform_now('', organization.id)
28
+
29
+ label = ForemanInventoryUpload::Async::UploadReportJob.output_label(organization.id)
30
+ progress_output = ForemanInventoryUpload::Async::ProgressOutput.get(label)
31
+ assert_match(/Skipping organization/, progress_output.full_output)
32
+ assert_match(/exit 1/, progress_output.status)
33
+ end
34
+ end
@@ -13,6 +13,8 @@ class MetadataGeneratorTest < ActiveSupport::TestCase
13
13
 
14
14
  assert_not_nil actual['report_id']
15
15
  assert_equal 'Satellite', actual['source']
16
+ assert_not_nil (actual_metadata = actual['source_metadata'])
17
+ assert_equal ForemanRhCloud::VERSION, actual_metadata['foreman_rh_cloud_version']
16
18
  assert_equal({}, actual['report_slices'])
17
19
  end
18
20
 
@@ -9,6 +9,7 @@ class ReportGeneratorTest < ActiveSupport::TestCase
9
9
 
10
10
  @host = FactoryBot.create(
11
11
  :host,
12
+ :redhat,
12
13
  :with_subscription,
13
14
  :with_content,
14
15
  content_view: cv.first,
@@ -264,6 +265,22 @@ class ReportGeneratorTest < ActiveSupport::TestCase
264
265
  assert_equal 1, generator.hosts_count
265
266
  end
266
267
 
268
+ test 'include also hosts with non-redhat OS' do
269
+ os = @host.operatingsystem
270
+ os.name = 'Centos'
271
+ os.save!
272
+
273
+ # make a_host last
274
+ batch = ForemanInventoryUpload::Generators::Queries.for_org(@host.organization_id).first
275
+ generator = create_generator(batch)
276
+
277
+ json_str = generator.render
278
+ actual = JSON.parse(json_str.join("\n"))
279
+
280
+ assert_equal 'slice_123', actual['report_slice_id']
281
+ assert_equal 1, generator.hosts_count
282
+ end
283
+
267
284
  test 'shows system_memory_bytes in bytes' do
268
285
  FactoryBot.create(:fact_value, fact_name: fact_names['memory::memtotal'], value: '1', host: @host)
269
286
 
@@ -19,6 +19,9 @@ Object {
19
19
  "insightsSyncEnabled": false,
20
20
  },
21
21
  },
22
+ "hostInsights": Object {
23
+ "hits": Array [],
24
+ },
22
25
  "inventoryUpload": Object {
23
26
  "accountsList": Object {
24
27
  "accounts": Object {},
@@ -8,7 +8,7 @@ const Toast = ({ syncHosts, disconnectHosts }) => {
8
8
  return (
9
9
  <span>
10
10
  <p>
11
- {__("Total org's hosts with subscriprion: ")}
11
+ {__('Hosts with subscription in organization: ')}
12
12
  <strong>{totalHosts}</strong>
13
13
  </p>
14
14
  <p>
@@ -27,7 +27,7 @@ const StatusChart = ({ completed }) => {
27
27
  <Grid.Col sm={4}>
28
28
  <div className="status-chart">
29
29
  <DonutChart
30
- id="donunt-chart-1"
30
+ id="donut-chart-1"
31
31
  size={{
32
32
  width: 210,
33
33
  height: 210,
@@ -43,7 +43,7 @@ exports[`StatusChart rendering render without Props 1`] = `
43
43
  "width": 11,
44
44
  }
45
45
  }
46
- id="donunt-chart-1"
46
+ id="donut-chart-1"
47
47
  legend={
48
48
  Object {
49
49
  "show": false,
@@ -1,4 +1,4 @@
1
- .tab-content {
1
+ .dashboard .tab-content {
2
2
  padding: 20px;
3
3
  min-height: 0;
4
4
 
@@ -1,7 +1,9 @@
1
1
  import ForemanInventoryUpload from './ForemanInventoryUpload';
2
2
  import InsightsCloudSync from './InsightsCloudSync';
3
+ import InsightsHostDetailsTab from './InsightsHostDetailsTab';
3
4
 
4
5
  export default [
5
6
  { name: 'ForemanInventoryUpload', type: ForemanInventoryUpload },
6
7
  { name: 'InsightsCloudSync', type: InsightsCloudSync },
8
+ { name: 'InsightsHostDetailsTab', type: InsightsHostDetailsTab },
7
9
  ];
@@ -1,10 +1,12 @@
1
1
  import { combineReducers } from 'redux';
2
2
  import inventoryUploadReducers from './ForemanInventoryUpload/ForemanInventoryUploadReducers';
3
3
  import insightsReducers from './InsightsCloudSync/InsightsCloudSyncReducers';
4
+ import { hostInsightsReducers } from './InsightsHostDetailsTab';
4
5
 
5
6
  export default {
6
7
  ForemanRhCloud: combineReducers({
7
8
  ...inventoryUploadReducers,
8
9
  ...insightsReducers,
10
+ ...hostInsightsReducers,
9
11
  }),
10
12
  };
@@ -1,5 +1,10 @@
1
1
  export const selectForemanRhCloud = state => state.ForemanRhCloud;
2
+
2
3
  export const selectForemanInventoryUpload = state =>
3
4
  selectForemanRhCloud(state).inventoryUpload;
5
+
4
6
  export const selectInsightsCloudSync = state =>
5
7
  selectForemanRhCloud(state).InsightsCloudSync;
8
+
9
+ export const selectHostInsights = state =>
10
+ selectForemanRhCloud(state).hostInsights;
@@ -1,10 +1,12 @@
1
1
  export const rhCloudStateWrapper = (
2
2
  inventoryState = {},
3
- insightsState = {}
3
+ insightsState = {},
4
+ hostInsightsState = {}
4
5
  ) => ({
5
6
  ForemanRhCloud: {
6
7
  inventoryUpload: { ...inventoryState },
7
8
  InsightsCloudSync: { ...insightsState },
9
+ hostInsights: { ...hostInsightsState },
8
10
  },
9
11
  });
10
12
 
@@ -13,3 +15,6 @@ export const inventoryStateWrapper = innerState =>
13
15
 
14
16
  export const insightsStateWrapper = innerState =>
15
17
  rhCloudStateWrapper({}, innerState);
18
+
19
+ export const hostInsightsStateWrapper = innerState =>
20
+ rhCloudStateWrapper({}, {}, innerState);
@@ -0,0 +1,64 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { orderBy } from 'lodash';
4
+ import { Grid, ListView, noop } from 'patternfly-react';
5
+ import ListItem from './components/ListItem';
6
+ import './InsightsTab.scss';
7
+
8
+ class InsightsHostDetailsTab extends React.Component {
9
+ componentDidMount() {
10
+ const { fetchHits, hostID } = this.props;
11
+ fetchHits(hostID);
12
+ }
13
+
14
+ render() {
15
+ const { hits } = this.props;
16
+
17
+ if (!hits.length) {
18
+ return <h2>No recommendations were found for this host!</h2>;
19
+ }
20
+ const hitsSorted = orderBy(hits, ['insights_hit.total_risk'], ['desc']);
21
+ const items = hitsSorted.map(
22
+ (
23
+ {
24
+ insights_hit: {
25
+ title,
26
+ total_risk: totalRisk,
27
+ results_url: resultsUrl,
28
+ solution_url: solutionUrl,
29
+ },
30
+ },
31
+ index
32
+ ) => (
33
+ <ListItem
34
+ key={index}
35
+ title={title}
36
+ totalRisk={totalRisk}
37
+ resultsUrl={resultsUrl}
38
+ solutionUrl={solutionUrl}
39
+ />
40
+ )
41
+ );
42
+ return (
43
+ <Grid.Row>
44
+ <Grid.Col xs={12}>
45
+ <h2>Recommendations</h2>
46
+ <ListView id="hits_list">{items}</ListView>
47
+ </Grid.Col>
48
+ </Grid.Row>
49
+ );
50
+ }
51
+ }
52
+
53
+ InsightsHostDetailsTab.propTypes = {
54
+ hostID: PropTypes.number.isRequired,
55
+ fetchHits: PropTypes.func,
56
+ hits: PropTypes.array,
57
+ };
58
+
59
+ InsightsHostDetailsTab.defaultProps = {
60
+ fetchHits: noop,
61
+ hits: [],
62
+ };
63
+
64
+ export default InsightsHostDetailsTab;
@@ -0,0 +1,86 @@
1
+ #host_details_insights_react_container {
2
+ #btn_toolbar {
3
+ float: right;
4
+ }
5
+
6
+ #btn_fullscreen {
7
+ margin-left: 5px;
8
+
9
+ .fa-arrows-alt {
10
+ margin-left: 5px;
11
+ }
12
+ }
13
+
14
+ h2 {
15
+ margin-top: 5px;
16
+ }
17
+
18
+ #hits_list {
19
+ max-height: 650px;
20
+ overflow-y: scroll;
21
+ overflow-x: hidden;
22
+ margin-top: 15px;
23
+
24
+ .list-view-pf-expand {
25
+ padding: 0;
26
+ }
27
+
28
+ .list-group-item-header {
29
+ .list-view-pf-expand .fa-angle-right {
30
+ margin-top: 6px;
31
+ }
32
+
33
+ .list-view-pf-main-info .list-view-pf-description {
34
+ width: 83%;
35
+
36
+ .list-group-item-heading {
37
+ width: 375px;
38
+ }
39
+
40
+ p {
41
+ margin: 0;
42
+ font-size: 14px;
43
+ }
44
+ }
45
+
46
+ .list-view-pf-additional-info {
47
+ .risk-label {
48
+ padding: 5px 8px;
49
+ border-radius: 12px;
50
+
51
+ &.Low {
52
+ background-color: #e7f1fa;
53
+ }
54
+
55
+ &.Moderate {
56
+ background-color: #fdf7e7;
57
+ }
58
+
59
+ &.Important {
60
+ background-color: #f9dddd;
61
+ }
62
+
63
+ &.Critical {
64
+ background-color: #ffecec;
65
+ }
66
+
67
+ p {
68
+ margin: 0;
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ &::-webkit-scrollbar {
75
+ width: 12px;
76
+ height: 12px;
77
+ }
78
+
79
+ &::-webkit-scrollbar-thumb {
80
+ background: #0e0e0e6e;
81
+ border-radius: 6px;
82
+ border: 3px solid transparent;
83
+ background-clip: content-box;
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,30 @@
1
+ import API from 'foremanReact/API';
2
+ import { insightsCloudUrl } from '../InsightsCloudSync/InsightsCloudSyncHelpers';
3
+ import {
4
+ INSIGHTS_HITS_REQUEST,
5
+ INSIGHTS_HITS_SUCCESS,
6
+ INSIGHTS_HITS_FAILURE,
7
+ } from './InsightsTabConstants';
8
+
9
+ export const fetchHits = hostID => async dispatch => {
10
+ try {
11
+ dispatch({
12
+ type: INSIGHTS_HITS_REQUEST,
13
+ payload: {},
14
+ });
15
+ const {
16
+ data: { hits },
17
+ } = await API.get(insightsCloudUrl(`hits/${hostID}`));
18
+ dispatch({
19
+ type: INSIGHTS_HITS_SUCCESS,
20
+ payload: { hits },
21
+ });
22
+ } catch (error) {
23
+ dispatch({
24
+ type: INSIGHTS_HITS_FAILURE,
25
+ payload: {
26
+ error: error.message,
27
+ },
28
+ });
29
+ }
30
+ };
@@ -0,0 +1,3 @@
1
+ export const INSIGHTS_HITS_REQUEST = 'INSIGHTS_HITS_REQUEST';
2
+ export const INSIGHTS_HITS_SUCCESS = 'INSIGHTS_HITS_SUCCESS';
3
+ export const INSIGHTS_HITS_FAILURE = 'INSIGHTS_HITS_FAILURE';
@@ -0,0 +1,26 @@
1
+ import Immutable from 'seamless-immutable';
2
+ import {
3
+ INSIGHTS_HITS_SUCCESS,
4
+ INSIGHTS_HITS_FAILURE,
5
+ } from './InsightsTabConstants';
6
+
7
+ const initialState = Immutable({
8
+ hits: [],
9
+ });
10
+
11
+ export default (state = initialState, action) => {
12
+ const { payload: { hits, error } = {} } = action;
13
+
14
+ switch (action.type) {
15
+ case INSIGHTS_HITS_SUCCESS:
16
+ return state.merge({
17
+ hits,
18
+ });
19
+ case INSIGHTS_HITS_FAILURE:
20
+ return state.merge({
21
+ error,
22
+ });
23
+ default:
24
+ return state;
25
+ }
26
+ };
@@ -0,0 +1,3 @@
1
+ import { selectHostInsights } from '../ForemanRhCloudSelectors';
2
+
3
+ export const selectHits = state => selectHostInsights(state).hits;
@@ -0,0 +1,25 @@
1
+ export const hostID = 1234;
2
+
3
+ export const hits = [
4
+ {
5
+ insights_hit: {
6
+ hostname: 'my-host.example.com',
7
+ rhel_version: '7.8',
8
+ uuid: '4739b323-a343-4e89-b71b-81991b8dc656',
9
+ last_seen: '2020-08-19T04:43:09.068706Z',
10
+ title:
11
+ 'New Ansible Engine packages are inaccessible when dedicated Ansible repo is not enabled',
12
+ solution_url: 'https://access.redhat.com/node/3359651',
13
+ total_risk: 2,
14
+ likelihood: 2,
15
+ publish_date: '2018-04-16T10:03:16Z',
16
+ results_url:
17
+ 'https://cloud.redhat.com/insights/advisor/recommendations/ansible_deprecated_repo%7CANSIBLE_DEPRECATED_REPO/4739b323-a343-4e89-b71b-81991b8dc656/',
18
+ },
19
+ },
20
+ ];
21
+
22
+ export const props = {
23
+ hostID,
24
+ hits,
25
+ };
@@ -0,0 +1,13 @@
1
+ import { testComponentSnapshotsWithFixtures } from 'react-redux-test-utils';
2
+
3
+ import InsightsTab from '../InsightsTab';
4
+ import { props } from './InsightsTab.fixtures';
5
+
6
+ const fixtures = {
7
+ 'render with props': props,
8
+ };
9
+
10
+ describe('InsightsTab', () => {
11
+ describe('rendering', () =>
12
+ testComponentSnapshotsWithFixtures(InsightsTab, fixtures));
13
+ });
@@ -0,0 +1,13 @@
1
+ import { testActionSnapshotWithFixtures } from 'react-redux-test-utils';
2
+ import API from 'foremanReact/API';
3
+ import { fetchHits } from '../InsightsTabActions';
4
+ import { hostID, hits } from './InsightsTab.fixtures';
5
+
6
+ jest.mock('foremanReact/API');
7
+ API.get.mockImplementation(async () => hits);
8
+
9
+ const fixtures = {
10
+ 'should fetchHits': () => fetchHits(hostID),
11
+ };
12
+
13
+ describe('InsightsTab actions', () => testActionSnapshotWithFixtures(fixtures));
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { IntegrationTestHelper } from 'react-redux-test-utils';
3
+
4
+ import InsightsTab from '../index';
5
+ import reducers from '../../ForemanRhCloudReducers';
6
+ import { hostID } from './InsightsTab.fixtures';
7
+
8
+ describe('InsightsTab integration test', () => {
9
+ it('should flow', async () => {
10
+ const integrationTestHelper = new IntegrationTestHelper(reducers);
11
+ const component = integrationTestHelper.mount(
12
+ <InsightsTab hostID={hostID} />
13
+ );
14
+ component.update();
15
+ /** Create a Flow test */
16
+ });
17
+ });
@@ -0,0 +1,35 @@
1
+ import { testReducerSnapshotWithFixtures } from 'react-redux-test-utils';
2
+ import reducer from '../InsightsTabReducer';
3
+ import { hits } from './InsightsTab.fixtures';
4
+ import {
5
+ INSIGHTS_HITS_REQUEST,
6
+ INSIGHTS_HITS_SUCCESS,
7
+ INSIGHTS_HITS_FAILURE,
8
+ } from '../InsightsTabConstants';
9
+
10
+ const fixtures = {
11
+ 'should return the initial state': {},
12
+ 'should handle INSIGHTS_HITS_REQUEST': {
13
+ action: {
14
+ type: INSIGHTS_HITS_REQUEST,
15
+ payload: {},
16
+ },
17
+ },
18
+ 'should handle INSIGHTS_HITS_SUCCESS': {
19
+ action: {
20
+ type: INSIGHTS_HITS_SUCCESS,
21
+ payload: { hits },
22
+ },
23
+ },
24
+ 'should handle INSIGHTS_HITS_FAILURE': {
25
+ action: {
26
+ type: INSIGHTS_HITS_FAILURE,
27
+ payload: {
28
+ error: 'some-error',
29
+ },
30
+ },
31
+ },
32
+ };
33
+
34
+ describe('AccountList reducer', () =>
35
+ testReducerSnapshotWithFixtures(reducer, fixtures));
@@ -0,0 +1,13 @@
1
+ import { testSelectorsSnapshotWithFixtures } from 'react-redux-test-utils';
2
+ import { hostInsightsStateWrapper } from '../../ForemanRhCloudTestHelpers';
3
+ import { hits } from './InsightsTab.fixtures';
4
+ import { selectHits } from '../InsightsTabSelectors';
5
+
6
+ const state = hostInsightsStateWrapper({ hits });
7
+
8
+ const fixtures = {
9
+ 'should return hits': () => selectHits(state),
10
+ };
11
+
12
+ describe('InsightsTab selectors', () =>
13
+ testSelectorsSnapshotWithFixtures(fixtures));
@@ -0,0 +1,30 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`InsightsTab rendering render with props 1`] = `
4
+ <Row
5
+ bsClass="row"
6
+ componentClass="div"
7
+ >
8
+ <Col
9
+ bsClass="col"
10
+ componentClass="div"
11
+ xs={12}
12
+ >
13
+ <h2>
14
+ Recommendations
15
+ </h2>
16
+ <ListView
17
+ className=""
18
+ id="hits_list"
19
+ >
20
+ <ListItem
21
+ key="0"
22
+ resultsUrl="https://cloud.redhat.com/insights/advisor/recommendations/ansible_deprecated_repo%7CANSIBLE_DEPRECATED_REPO/4739b323-a343-4e89-b71b-81991b8dc656/"
23
+ solutionUrl="https://access.redhat.com/node/3359651"
24
+ title="New Ansible Engine packages are inaccessible when dedicated Ansible repo is not enabled"
25
+ totalRisk={2}
26
+ />
27
+ </ListView>
28
+ </Col>
29
+ </Row>
30
+ `;
@@ -0,0 +1,20 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`InsightsTab actions should fetchHits 1`] = `
4
+ Array [
5
+ Array [
6
+ Object {
7
+ "payload": Object {},
8
+ "type": "INSIGHTS_HITS_REQUEST",
9
+ },
10
+ ],
11
+ Array [
12
+ Object {
13
+ "payload": Object {
14
+ "error": "Cannot read property 'hits' of undefined",
15
+ },
16
+ "type": "INSIGHTS_HITS_FAILURE",
17
+ },
18
+ ],
19
+ ]
20
+ `;
@@ -0,0 +1,41 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`AccountList reducer should handle INSIGHTS_HITS_FAILURE 1`] = `
4
+ Object {
5
+ "error": "some-error",
6
+ "hits": Array [],
7
+ }
8
+ `;
9
+
10
+ exports[`AccountList reducer should handle INSIGHTS_HITS_REQUEST 1`] = `
11
+ Object {
12
+ "hits": Array [],
13
+ }
14
+ `;
15
+
16
+ exports[`AccountList reducer should handle INSIGHTS_HITS_SUCCESS 1`] = `
17
+ Object {
18
+ "hits": Array [
19
+ Object {
20
+ "insights_hit": Object {
21
+ "hostname": "my-host.example.com",
22
+ "last_seen": "2020-08-19T04:43:09.068706Z",
23
+ "likelihood": 2,
24
+ "publish_date": "2018-04-16T10:03:16Z",
25
+ "results_url": "https://cloud.redhat.com/insights/advisor/recommendations/ansible_deprecated_repo%7CANSIBLE_DEPRECATED_REPO/4739b323-a343-4e89-b71b-81991b8dc656/",
26
+ "rhel_version": "7.8",
27
+ "solution_url": "https://access.redhat.com/node/3359651",
28
+ "title": "New Ansible Engine packages are inaccessible when dedicated Ansible repo is not enabled",
29
+ "total_risk": 2,
30
+ "uuid": "4739b323-a343-4e89-b71b-81991b8dc656",
31
+ },
32
+ },
33
+ ],
34
+ }
35
+ `;
36
+
37
+ exports[`AccountList reducer should return the initial state 1`] = `
38
+ Object {
39
+ "hits": Array [],
40
+ }
41
+ `;
@@ -0,0 +1,20 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`InsightsTab selectors should return hits 1`] = `
4
+ Array [
5
+ Object {
6
+ "insights_hit": Object {
7
+ "hostname": "my-host.example.com",
8
+ "last_seen": "2020-08-19T04:43:09.068706Z",
9
+ "likelihood": 2,
10
+ "publish_date": "2018-04-16T10:03:16Z",
11
+ "results_url": "https://cloud.redhat.com/insights/advisor/recommendations/ansible_deprecated_repo%7CANSIBLE_DEPRECATED_REPO/4739b323-a343-4e89-b71b-81991b8dc656/",
12
+ "rhel_version": "7.8",
13
+ "solution_url": "https://access.redhat.com/node/3359651",
14
+ "title": "New Ansible Engine packages are inaccessible when dedicated Ansible repo is not enabled",
15
+ "total_risk": 2,
16
+ "uuid": "4739b323-a343-4e89-b71b-81991b8dc656",
17
+ },
18
+ },
19
+ ]
20
+ `;
@@ -0,0 +1,69 @@
1
+ import React, { Fragment } from 'react';
2
+ import { ListView, Icon } from 'patternfly-react';
3
+ import PropTypes from 'prop-types';
4
+
5
+ const labelMapper = {
6
+ 1: 'Low',
7
+ 2: 'Moderate',
8
+ 3: 'Important',
9
+ 4: 'Critical',
10
+ };
11
+
12
+ const ListItem = ({ title, totalRisk, resultsUrl, solutionUrl }) => {
13
+ const heading = (
14
+ <p className="ellipsis list-item-heading" title={title}>
15
+ {title}
16
+ </p>
17
+ );
18
+
19
+ const riskLabel = labelMapper[totalRisk];
20
+ const additionalInfo = [
21
+ <span key={`risk-info-${title}`} className={`risk-label ${riskLabel}`}>
22
+ <p>{riskLabel}</p>
23
+ </span>,
24
+ ];
25
+
26
+ const knowledgebaseLink = solutionUrl && (
27
+ <p>
28
+ <a href={solutionUrl} target="_blank" rel="noopener noreferrer">
29
+ Knowledgebase article <Icon name="external-link" />
30
+ </a>
31
+ </p>
32
+ );
33
+
34
+ const insightsCloudLink = resultsUrl && (
35
+ <p>
36
+ <a href={resultsUrl} target="_blank" rel="noopener noreferrer">
37
+ Read more about it in RH cloud insights <Icon name="external-link" />
38
+ </a>
39
+ </p>
40
+ );
41
+
42
+ return (
43
+ <ListView.Item
44
+ heading={heading}
45
+ additionalInfo={additionalInfo}
46
+ hideCloseIcon
47
+ >
48
+ <Fragment>
49
+ <p>{title}</p>
50
+ {knowledgebaseLink}
51
+ {insightsCloudLink}
52
+ </Fragment>
53
+ </ListView.Item>
54
+ );
55
+ };
56
+
57
+ ListItem.propTypes = {
58
+ title: PropTypes.string.isRequired,
59
+ totalRisk: PropTypes.number.isRequired,
60
+ resultsUrl: PropTypes.string,
61
+ solutionUrl: PropTypes.string,
62
+ };
63
+
64
+ ListItem.defaultProps = {
65
+ resultsUrl: '',
66
+ solutionUrl: '',
67
+ };
68
+
69
+ export default ListItem;
@@ -0,0 +1 @@
1
+ export { default } from './ListItem';
@@ -0,0 +1,20 @@
1
+ import { bindActionCreators } from 'redux';
2
+ import { connect } from 'react-redux';
3
+ import InsightsTab from './InsightsTab';
4
+ import * as actions from './InsightsTabActions';
5
+ import reducer from './InsightsTabReducer';
6
+ import { selectHits } from './InsightsTabSelectors';
7
+
8
+ // map state to props
9
+ const mapStateToProps = state => ({
10
+ hits: selectHits(state),
11
+ });
12
+
13
+ // map action dispatchers to props
14
+ const mapDispatchToProps = dispatch => bindActionCreators(actions, dispatch);
15
+
16
+ // export reducers
17
+ export const hostInsightsReducers = { hostInsights: reducer };
18
+
19
+ // export connected component
20
+ export default connect(mapStateToProps, mapDispatchToProps)(InsightsTab);
@@ -11,6 +11,7 @@ Object {
11
11
  "InsightsCloudSync": Object {
12
12
  "insightsChild": Object {},
13
13
  },
14
+ "hostInsights": Object {},
14
15
  "inventoryUpload": Object {
15
16
  "inventoryChild": Object {},
16
17
  },
@@ -6,6 +6,7 @@ Object {
6
6
  "InsightsCloudSync": Object {
7
7
  "insightsChild": Object {},
8
8
  },
9
+ "hostInsights": Object {},
9
10
  "inventoryUpload": Object {},
10
11
  },
11
12
  }
@@ -15,6 +16,7 @@ exports[`ForemanRhCloud helpers should return inventory wrapper 1`] = `
15
16
  Object {
16
17
  "ForemanRhCloud": Object {
17
18
  "InsightsCloudSync": Object {},
19
+ "hostInsights": Object {},
18
20
  "inventoryUpload": Object {
19
21
  "inventoryChild": Object {},
20
22
  },
@@ -28,6 +30,7 @@ Object {
28
30
  "InsightsCloudSync": Object {
29
31
  "insightsChild": Object {},
30
32
  },
33
+ "hostInsights": Object {},
31
34
  "inventoryUpload": Object {
32
35
  "inventoryChild": Object {},
33
36
  },
@@ -4,7 +4,7 @@ export const withCardsDecorator = storyFn => (
4
4
  <div
5
5
  style={{
6
6
  width: '100%',
7
- height: '100vh',
7
+ height: '100%',
8
8
  backgroundColor: '#F5F5F5',
9
9
  padding: '50px',
10
10
  }}
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_rh_cloud
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.10
4
+ version: 1.0.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Foreman Red Hat Cloud team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-04 00:00:00.000000000 Z
11
+ date: 2020-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: katello
@@ -109,10 +109,12 @@ files:
109
109
  - app/controllers/foreman_inventory_upload/tasks_controller.rb
110
110
  - app/controllers/foreman_inventory_upload/uploads_controller.rb
111
111
  - app/controllers/foreman_rh_cloud/react_controller.rb
112
+ - app/controllers/insights_cloud/hits_controller.rb
112
113
  - app/controllers/insights_cloud/settings_controller.rb
113
114
  - app/controllers/insights_cloud/tasks_controller.rb
114
115
  - app/helpers/foreman_inventory_upload_helper.rb
115
116
  - app/helpers/foreman_inventory_upload_host_helper.rb
117
+ - app/models/concerns/rh_cloud_host.rb
116
118
  - app/models/insights_facet.rb
117
119
  - app/models/insights_hit.rb
118
120
  - app/models/inventory_sync/inventory_status.rb
@@ -120,6 +122,7 @@ files:
120
122
  - app/overrides/hosts_list.rb
121
123
  - app/views/foreman_rh_cloud/react/insights_cloud.html.erb
122
124
  - app/views/foreman_rh_cloud/react/inventory_upload.html.erb
125
+ - app/views/hosts/_insights_tab.html.erb
123
126
  - app/views/layouts/foreman_rh_cloud/application.html.erb
124
127
  - config/routes.rb
125
128
  - db/migrate/20191215104806_create_insights_hits.foreman_inventory_upload.rb
@@ -150,7 +153,8 @@ files:
150
153
  - lib/inventory_sync/async/host_result.rb
151
154
  - lib/inventory_sync/async/inventory_full_sync.rb
152
155
  - lib/tasks/foreman_rh_cloud_tasks.rake
153
- - lib/tasks/generator.rake
156
+ - lib/tasks/insights.rake
157
+ - lib/tasks/rh_cloud_inventory.rake
154
158
  - locale/Makefile
155
159
  - locale/en/foreman_rh_cloud.po
156
160
  - locale/foreman_rh_cloud.pot
@@ -164,6 +168,7 @@ files:
164
168
  - test/factories/inventory_upload_factories.rb
165
169
  - test/jobs/insights_full_sync_test.rb
166
170
  - test/jobs/inventory_full_sync_test.rb
171
+ - test/jobs/upload_report_job_test.rb
167
172
  - test/test_plugin_helper.rb
168
173
  - test/unit/archived_report_generator_test.rb
169
174
  - test/unit/fact_helpers_test.rb
@@ -458,6 +463,25 @@ files:
458
463
  - webpack/InsightsCloudSync/__tests__/InsightsCloudSyncHelpers.test.js
459
464
  - webpack/InsightsCloudSync/__tests__/__snapshots__/InsightsCloudSyncHelpers.test.js.snap
460
465
  - webpack/InsightsCloudSync/index.js
466
+ - webpack/InsightsHostDetailsTab/InsightsTab.js
467
+ - webpack/InsightsHostDetailsTab/InsightsTab.scss
468
+ - webpack/InsightsHostDetailsTab/InsightsTabActions.js
469
+ - webpack/InsightsHostDetailsTab/InsightsTabConstants.js
470
+ - webpack/InsightsHostDetailsTab/InsightsTabReducer.js
471
+ - webpack/InsightsHostDetailsTab/InsightsTabSelectors.js
472
+ - webpack/InsightsHostDetailsTab/__tests__/InsightsTab.fixtures.js
473
+ - webpack/InsightsHostDetailsTab/__tests__/InsightsTab.test.js
474
+ - webpack/InsightsHostDetailsTab/__tests__/InsightsTabActions.test.js
475
+ - webpack/InsightsHostDetailsTab/__tests__/InsightsTabIntegration.test.js
476
+ - webpack/InsightsHostDetailsTab/__tests__/InsightsTabReducer.test.js
477
+ - webpack/InsightsHostDetailsTab/__tests__/InsightsTabSelectors.test.js
478
+ - webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTab.test.js.snap
479
+ - webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabActions.test.js.snap
480
+ - webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabReducer.test.js.snap
481
+ - webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabSelectors.test.js.snap
482
+ - webpack/InsightsHostDetailsTab/components/ListItem/ListItem.js
483
+ - webpack/InsightsHostDetailsTab/components/ListItem/index.js
484
+ - webpack/InsightsHostDetailsTab/index.js
461
485
  - webpack/__mocks__/foremanReact/API.js
462
486
  - webpack/__mocks__/foremanReact/common/I18n.js
463
487
  - webpack/__mocks__/foremanReact/common/helpers.js
@@ -498,23 +522,24 @@ required_rubygems_version: !ruby/object:Gem::Requirement
498
522
  - !ruby/object:Gem::Version
499
523
  version: '0'
500
524
  requirements: []
501
- rubygems_version: 3.0.6
525
+ rubygems_version: 3.0.8
502
526
  signing_key:
503
527
  specification_version: 4
504
528
  summary: Summary of ForemanRhCloud.
505
529
  test_files:
506
- - test/controllers/uploads_controller_test.rb
507
- - test/controllers/insights_sync/settings_controller_test.rb
508
- - test/controllers/accounts_controller_test.rb
509
- - test/controllers/reports_controller_test.rb
510
530
  - test/test_plugin_helper.rb
511
- - test/jobs/insights_full_sync_test.rb
512
- - test/jobs/inventory_full_sync_test.rb
513
531
  - test/factories/inventory_upload_factories.rb
514
532
  - test/factories/insights_factories.rb
515
- - test/unit/shell_process_job_test.rb
533
+ - test/controllers/reports_controller_test.rb
534
+ - test/controllers/uploads_controller_test.rb
535
+ - test/controllers/accounts_controller_test.rb
536
+ - test/controllers/insights_sync/settings_controller_test.rb
537
+ - test/unit/slice_generator_test.rb
516
538
  - test/unit/metadata_generator_test.rb
539
+ - test/unit/shell_process_job_test.rb
517
540
  - test/unit/insights_facet_test.rb
518
- - test/unit/fact_helpers_test.rb
519
541
  - test/unit/archived_report_generator_test.rb
520
- - test/unit/slice_generator_test.rb
542
+ - test/unit/fact_helpers_test.rb
543
+ - test/jobs/inventory_full_sync_test.rb
544
+ - test/jobs/insights_full_sync_test.rb
545
+ - test/jobs/upload_report_job_test.rb