foreman_rh_cloud 12.2.13 → 12.2.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/foreman_rh_cloud/registration_manager_extensions.rb +2 -2
  3. data/app/controllers/concerns/insights_cloud/package_profile_upload_extensions.rb +3 -0
  4. data/app/controllers/foreman_inventory_upload/accounts_controller.rb +1 -1
  5. data/app/controllers/foreman_inventory_upload/uploads_controller.rb +1 -1
  6. data/app/controllers/insights_cloud/ui_requests_controller.rb +2 -3
  7. data/app/models/concerns/rh_cloud_host.rb +14 -0
  8. data/app/models/foreman_rh_cloud/ping.rb +2 -1
  9. data/app/services/foreman_rh_cloud/tags_auth.rb +13 -3
  10. data/app/views/api/v2/advisor_engine/host_details.json.rabl +1 -3
  11. data/app/views/api/v2/hosts/insights/base.rabl +3 -2
  12. data/lib/foreman_inventory_upload/async/queue_for_upload_job.rb +1 -23
  13. data/lib/foreman_inventory_upload/async/upload_report_direct_job.rb +200 -0
  14. data/lib/foreman_inventory_upload.rb +0 -4
  15. data/lib/foreman_rh_cloud/plugin.rb +11 -4
  16. data/lib/foreman_rh_cloud/version.rb +1 -1
  17. data/lib/foreman_rh_cloud.rb +3 -3
  18. data/lib/insights_cloud/async/connector_playbook_execution_reporter_task.rb +3 -3
  19. data/lib/inventory_sync/async/inventory_hosts_sync.rb +0 -2
  20. data/package.json +1 -1
  21. data/test/controllers/accounts_controller_test.rb +1 -1
  22. data/test/controllers/insights_cloud/api/advisor_engine_controller_test.rb +28 -1
  23. data/test/controllers/insights_cloud/ui_requests_controller_test.rb +26 -0
  24. data/test/controllers/uploads_controller_test.rb +1 -1
  25. data/test/factories/insights_factories.rb +29 -0
  26. data/test/jobs/queue_for_upload_job_test.rb +1 -12
  27. data/test/jobs/upload_report_direct_job_test.rb +399 -0
  28. data/test/unit/foreman_rh_cloud_iop_metadata_test.rb +200 -0
  29. data/test/unit/lib/foreman_rh_cloud/registration_manager_extensions_test.rb +4 -42
  30. data/test/unit/rh_cloud_host_test.rb +191 -0
  31. data/test/unit/services/foreman_rh_cloud/tags_auth_test.rb +14 -0
  32. data/webpack/ForemanColumnExtensions/index.js +2 -0
  33. data/webpack/ForemanInventoryUpload/Components/PageHeader/PageHeader.js +24 -17
  34. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageHeader.test.js +178 -8
  35. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageTitle.test.js.snap +1 -1
  36. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/ToolbarButtons.js +3 -1
  37. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/__tests__/ToolbarButtons.test.js +69 -51
  38. data/webpack/ForemanInventoryUpload/ForemanInventoryHelpers.js +1 -1
  39. data/webpack/ForemanInventoryUpload/__tests__/__snapshots__/ForemanInventoryHelpers.test.js.snap +1 -1
  40. data/webpack/InsightsCloudSync/Components/InsightsSettings/InsightsSettings.js +3 -3
  41. data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTable.js +3 -9
  42. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTable.test.js +24 -4
  43. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.js +2 -2
  44. data/webpack/InsightsCloudSync/Components/ToolbarDropdown.js +3 -3
  45. data/webpack/InsightsCloudSync/InsightsCloudSync.js +3 -3
  46. data/webpack/InsightsCloudSync/InsightsCloudSync.test.js +10 -0
  47. data/webpack/InsightsCloudSync/__snapshots__/InsightsCloudSync.test.js.snap +1 -1
  48. data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +5 -5
  49. data/webpack/InsightsVulnerabilityHostIndexExtensions/CVECountCell.js +2 -2
  50. data/webpack/InsightsVulnerabilityHostIndexExtensions/__tests__/CVECountCell.test.js +77 -22
  51. data/webpack/common/Hooks/ConfigHooks.js +3 -16
  52. metadata +9 -9
  53. data/lib/foreman_inventory_upload/async/upload_report_job.rb +0 -97
  54. data/lib/foreman_inventory_upload/scripts/uploader.sh.erb +0 -55
  55. data/test/jobs/upload_report_job_test.rb +0 -38
  56. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageHeader.test.js.snap +0 -36
  57. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTable.test.js.snap +0 -112
  58. data/webpack/__mocks__/foremanReact/common/hooks/API/APIHooks.js +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27458c63e65391eef2cce60a1d6d0bab3ec76ebaac4cc844420e38f5b0074794
4
- data.tar.gz: 157d7d238ca974f97e3253a7363f6b5eb8bd5fa378adf975d2e2816e947df8cc
3
+ metadata.gz: 298be7d24bfc6e80e79eee94df423d76892fcf4f45a8750b940776f0647e4761
4
+ data.tar.gz: 6c3f86b461e2697775133e0c9483eefc09b8ae35fc7912810f80f43a66f78512
5
5
  SHA512:
6
- metadata.gz: 90533b7fec623776352cc2d0791556f78b6910913bfe8c2d1869fc2d10df0946d0e66dcd37c4d8788bc3451aa3e4c5c6991e9ebceedc19c620585d7e68a55f5b
7
- data.tar.gz: 958964446134a0673007003fe45b4bda54add3a81d2b9c4f98799477e0f77a6df8cd5c82371b0794db7216e6c60b362ed686779c84beec050e79e5b3967fbb79
6
+ metadata.gz: 738e2cd68221abed620727ded441caee536a19f09ab5d668eb223dedbc37266e9813415ac3218d54e6b6a19e3f54e824a312a7304baa61d64e74f168544c6cd4
7
+ data.tar.gz: 44b1b35ecd68218582099ddff9fc1b94a56108a585eb9799adee89e615608ab4588d20bde9e0d53aa9d36de82cd58009cb0cc7dd5bdaa32778a402ee4b2314a9
@@ -13,13 +13,13 @@ module ForemanRhCloud
13
13
  host.reload
14
14
 
15
15
  # Only delete from HBI in IoP mode (hosted mode uses async job for cleanup)
16
- hbi_host_destroy(host) if ForemanRhCloud.with_iop_smart_proxy? && !organization_destroy && host.insights_facet&.uuid&.presence
16
+ hbi_host_destroy(host) if ForemanRhCloud.with_iop_smart_proxy? && !organization_destroy && host.insights_uuid.presence
17
17
  host.insights&.destroy!
18
18
  super(host, options)
19
19
  end
20
20
 
21
21
  def hbi_host_destroy(host)
22
- uuid = host.insights_facet.uuid
22
+ uuid = host.insights_uuid
23
23
  logger.debug "Unregistering host #{uuid} from HBI"
24
24
  execute_cloud_request(
25
25
  organization: host.organization,
@@ -21,6 +21,9 @@ module InsightsCloud
21
21
  @host.id
22
22
  )
23
23
 
24
+ # Ensure insights UUID matches subscription UUID (only runs in IoP mode per method guard above)
25
+ @host.ensure_iop_insights_uuid
26
+
24
27
  # in IoP case, the hosts are identified by the sub-man ID, and we can assume they already
25
28
  # exist in the local inventory. This will also handle facet creation for new hosts.
26
29
  return if @host.insights
@@ -7,7 +7,7 @@ module ForemanInventoryUpload
7
7
  accounts = Hash[
8
8
  labels.map do |id, label|
9
9
  generate_report_status = status_for(id, ForemanInventoryUpload::Async::GenerateReportJob)
10
- upload_report_status = status_for(id, ForemanInventoryUpload::Async::UploadReportJob)
10
+ upload_report_status = status_for(id, ForemanInventoryUpload::Async::UploadReportDirectJob)
11
11
  report_file_paths = ForemanInventoryUpload.report_file_paths(id)
12
12
 
13
13
  [
@@ -6,7 +6,7 @@ module ForemanInventoryUpload
6
6
  before_action :require_non_iop_smart_proxy, only: [:enable_cloud_connector]
7
7
 
8
8
  def last
9
- label = ForemanInventoryUpload::Async::UploadReportJob.output_label(params[:organization_id])
9
+ label = ForemanInventoryUpload::Async::UploadReportDirectJob.output_label(params[:organization_id])
10
10
  output = ForemanInventoryUpload::Async::ProgressOutput.get(label)&.full_output
11
11
 
12
12
  render json: {
@@ -2,7 +2,7 @@ module InsightsCloud
2
2
  class UIRequestsController < ::ApplicationController
3
3
  layout false
4
4
 
5
- before_action :ensure_org, :ensure_loc, :only => [:forward_request]
5
+ before_action :ensure_org, :find_location, :only => [:forward_request]
6
6
 
7
7
  # The method that "proxies" requests over to Cloud
8
8
  def forward_request
@@ -93,9 +93,8 @@ module InsightsCloud
93
93
  return render_message 'Organization not found or invalid', :status => 400 unless @organization
94
94
  end
95
95
 
96
- def ensure_loc
96
+ def find_location
97
97
  @location = Location.current
98
- return render_message 'Location not found or invalid', :status => 400 unless @location
99
98
  end
100
99
 
101
100
  def base_url
@@ -26,5 +26,19 @@ module RhCloudHost
26
26
  def insights_facet
27
27
  insights
28
28
  end
29
+
30
+ # In IoP, read directly from the subscription facet to avoid stale data (see comment on ensure_iop_insights_uuid)
31
+ def insights_uuid
32
+ ForemanRhCloud.with_iop_smart_proxy? ? subscription_facet&.uuid : insights_facet&.uuid
33
+ end
34
+
35
+ # In non-IoP, insights_facet uuids are assigned by Hosted.
36
+ # In IoP, insights_facet uuids must match Katello subscription_facet uuids.
37
+ # If the host was previously registered to hosted Insights,
38
+ # we need to correct its uuid.
39
+ def ensure_iop_insights_uuid
40
+ return unless insights_facet.present? && subscription_facet.present? && insights_facet.uuid != subscription_facet.uuid
41
+ insights_facet.update!(uuid: subscription_facet.uuid)
42
+ end
29
43
  end
30
44
  end
@@ -7,7 +7,7 @@ module ForemanRhCloud
7
7
  include ForemanRhCloud::CertAuth
8
8
 
9
9
  def iop_smart_proxy_url
10
- @iop_smart_proxy_url ||= ForemanRhCloud.iop_smart_proxy.url
10
+ ForemanRhCloud.iop_smart_proxy&.url
11
11
  end
12
12
 
13
13
  def service_urls
@@ -33,6 +33,7 @@ module ForemanRhCloud
33
33
  end
34
34
 
35
35
  def ping
36
+ return {} unless ForemanRhCloud.with_iop_smart_proxy?
36
37
  ping_services
37
38
  end
38
39
 
@@ -20,7 +20,8 @@ module ForemanRhCloud
20
20
  end
21
21
 
22
22
  def update_tag
23
- logger.debug("Updating tags for user: #{@user}, org: #{@org.name}, loc: #{@loc.name}")
23
+ loc_name = location_name_for_tag
24
+ logger.debug("Updating tags for user: #{@user}, org: #{@org.name}, loc: #{loc_name}")
24
25
 
25
26
  payload = tags_query_payload
26
27
  params = {
@@ -36,7 +37,9 @@ module ForemanRhCloud
36
37
  end
37
38
 
38
39
  def allowed_hosts
39
- Host.authorized_as(@user, nil, nil).where(organization: @org, location: @loc).joins(:subscription_facet).pluck('katello_subscription_facets.uuid')
40
+ query = Host.authorized_as(@user, nil, nil).where(organization: @org)
41
+ query = query.where(location: @loc) if @loc
42
+ query.joins(:subscription_facet).pluck('katello_subscription_facets.uuid')
40
43
  end
41
44
 
42
45
  def tags_query_payload
@@ -47,11 +50,18 @@ module ForemanRhCloud
47
50
  end
48
51
 
49
52
  def tag_value
50
- "U:\"#{@user.login}\"O:\"#{@org.name}\"L:\"#{@loc.name}\""
53
+ location_part = "L:\"#{location_name_for_tag}\""
54
+ "U:\"#{@user.login}\"O:\"#{@org.name}\"#{location_part}"
51
55
  end
52
56
 
53
57
  def auth_tag
54
58
  "#{TAG_NAME}=#{tag_value}"
55
59
  end
60
+
61
+ private
62
+
63
+ def location_name_for_tag
64
+ @loc ? @loc.name : '*'
65
+ end
56
66
  end
57
67
  end
@@ -1,9 +1,7 @@
1
1
  collection @hosts
2
2
 
3
3
  attributes :name
4
- node :insights_uuid do |host|
5
- host.insights_facet&.uuid
6
- end
4
+ node :insights_uuid, &:insights_uuid
7
5
  node :insights_hit_details do |host|
8
6
  host&.facts('insights::hit_details')&.values&.first
9
7
  end
@@ -1,5 +1,6 @@
1
- attributes :uuid
2
-
1
+ node :uuid do |facet|
2
+ facet&.host&.insights_uuid
3
+ end
3
4
  node :insights_hit_details do |facet|
4
5
  facet&.host&.facts('insights::hit_details')&.values&.first
5
6
  end
@@ -9,7 +9,6 @@ module ForemanInventoryUpload
9
9
  def run
10
10
  logger.debug('Ensuring objects')
11
11
  ensure_ouput_folder
12
- ensure_output_script
13
12
  logger.debug("Copying #{report_file} to #{uploads_folder}")
14
13
  enqueued_file_name = File.join(uploads_folder, report_file)
15
14
  FileUtils.mv(File.join(base_folder, report_file), enqueued_file_name)
@@ -22,31 +21,10 @@ module ForemanInventoryUpload
22
21
  @uploads_folder ||= ForemanInventoryUpload.uploads_folder
23
22
  end
24
23
 
25
- def script_file
26
- @script_file ||= File.join(uploads_folder, ForemanInventoryUpload.upload_script_file)
27
- end
28
-
29
24
  def ensure_ouput_folder
30
25
  FileUtils.mkdir_p(uploads_folder)
31
26
  end
32
27
 
33
- def ensure_output_script
34
- return if File.exist?(script_file)
35
-
36
- script_source = File.join(ForemanRhCloud::Engine.root, 'lib/foreman_inventory_upload/scripts/uploader.sh.erb')
37
-
38
- template_src = Foreman::Renderer::Source::String.new(content: File.read(script_source))
39
- scope = Foreman::Renderer::Scope::Base.new(
40
- source: template_src,
41
- variables: {
42
- upload_url: ForemanInventoryUpload.upload_url,
43
- }
44
- )
45
- script_source = Foreman::Renderer.render(template_src, scope)
46
- File.write(script_file, script_source)
47
- FileUtils.chmod('+x', script_file)
48
- end
49
-
50
28
  def logger
51
29
  Foreman::Logging.logger('background')
52
30
  end
@@ -60,7 +38,7 @@ module ForemanInventoryUpload
60
38
  end
61
39
 
62
40
  def plan_upload_report(enqueued_file_name, organization_id)
63
- plan_action(UploadReportJob, enqueued_file_name, organization_id)
41
+ plan_action(UploadReportDirectJob, enqueued_file_name, organization_id)
64
42
  end
65
43
  end
66
44
  end
@@ -0,0 +1,200 @@
1
+ require 'tempfile'
2
+ require 'rest-client'
3
+
4
+ module ForemanInventoryUpload
5
+ module Async
6
+ class UploadReportDirectJob < ::Actions::EntryAction
7
+ include AsyncHelpers
8
+ include ::ForemanRhCloud::Async::ExponentialBackoff
9
+ include ::ForemanRhCloud::CloudRequest
10
+
11
+ # Wrapper class to avoid monkey-patching File for multipart uploads
12
+ class FileUpload
13
+ attr_reader :file, :content_type
14
+
15
+ def initialize(file, content_type:)
16
+ @file = file
17
+ @content_type = content_type
18
+ end
19
+
20
+ def read(*args)
21
+ @file.read(*args)
22
+ end
23
+
24
+ def path
25
+ @file.path
26
+ end
27
+
28
+ def respond_to_missing?(method_name, include_private = false)
29
+ @file.respond_to?(method_name, include_private) || super
30
+ end
31
+
32
+ def method_missing(method_name, *args, &block)
33
+ if @file.respond_to?(method_name)
34
+ @file.send(method_name, *args, &block)
35
+ else
36
+ super
37
+ end
38
+ end
39
+ end
40
+
41
+ def self.output_label(label)
42
+ "upload_for_#{label}"
43
+ end
44
+
45
+ def plan(filename, organization_id)
46
+ # NOTE: This implementation assumes a single organization will not trigger multiple
47
+ # concurrent uploads. The instance_label is derived from organization_id alone, which
48
+ # means concurrent uploads for the same org would share ProgressOutput storage.
49
+ # This matches the pattern in GenerateReportJob. A full fix for thread-safety
50
+ # requires UI changes to display multiple concurrent tasks per org (tracked for PR #2).
51
+ label = UploadReportDirectJob.output_label(organization_id)
52
+ clear_task_output(label)
53
+ plan_self(
54
+ instance_label: label,
55
+ filename: filename,
56
+ organization_id: organization_id
57
+ )
58
+ end
59
+
60
+ def try_execute
61
+ if content_disconnected?
62
+ progress_output do |progress_output|
63
+ progress_output.write_line("Report was not moved and upload was canceled because connection to Insights is not enabled. Report location: #{filename}.")
64
+ progress_output.status = "Task aborted, exit 1"
65
+ done!
66
+ end
67
+ return
68
+ end
69
+
70
+ unless organization.owner_details&.dig('upstreamConsumer', 'idCert')
71
+ logger.info("Skipping organization '#{organization}', no candlepin certificate defined.")
72
+ progress_output do |progress_output|
73
+ progress_output.write_line("Skipping organization #{organization}, no candlepin certificate defined.")
74
+ progress_output.status = "Task aborted, exit 1"
75
+ done!
76
+ end
77
+ return
78
+ end
79
+
80
+ Tempfile.create([organization.name, '.pem']) do |cer_file|
81
+ cer_file.write(certificate[:cert])
82
+ cer_file.write(certificate[:key])
83
+ cer_file.flush
84
+ upload_report(cer_file.path)
85
+ end
86
+
87
+ done!
88
+ end
89
+
90
+ def upload_report(cer_path)
91
+ progress_output do |progress_output|
92
+ progress_output.write_line("Uploading report for organization #{organization.label}...")
93
+ progress_output.status = "Running upload"
94
+
95
+ begin
96
+ upload_file(cer_path)
97
+ progress_output.write_line("Upload completed successfully")
98
+ move_to_done_folder
99
+ progress_output.write_line("Uploaded file moved to done/ folder")
100
+ progress_output.status = "pid #{Process.pid} exit 0"
101
+ rescue StandardError => e
102
+ progress_output.write_line("Upload failed: #{e.message}")
103
+ progress_output.status = "pid #{Process.pid} exit 1"
104
+ raise
105
+ end
106
+ end
107
+ end
108
+
109
+ def upload_file(cer_path)
110
+ cert_content = File.read(cer_path)
111
+
112
+ File.open(filename, 'rb') do |file|
113
+ # Wrap file with FileUpload class for RestClient multipart handling
114
+ # RestClient requires objects with :read, :path, and :content_type methods
115
+ wrapped_file = FileUpload.new(file, content_type: 'application/vnd.redhat.qpc.tar+tgz')
116
+
117
+ response = execute_cloud_request(
118
+ method: :post,
119
+ url: ForemanInventoryUpload.upload_url,
120
+ payload: {
121
+ multipart: true,
122
+ file: wrapped_file,
123
+ },
124
+ headers: {
125
+ 'X-Org-Id' => organization.label,
126
+ },
127
+ ssl_client_cert: OpenSSL::X509::Certificate.new(cert_content),
128
+ ssl_client_key: OpenSSL::PKey::RSA.new(cert_content),
129
+ timeout: 600,
130
+ open_timeout: 60
131
+ )
132
+
133
+ logger.debug("Upload response code: #{response.code}")
134
+ end
135
+ end
136
+
137
+ def move_to_done_folder
138
+ FileUtils.mkdir_p(ForemanInventoryUpload.done_folder)
139
+ done_file = ForemanInventoryUpload.done_file_path(File.basename(filename))
140
+ FileUtils.mv(filename, done_file)
141
+ logger.debug("Moved #{filename} to #{done_file}")
142
+ end
143
+
144
+ def certificate
145
+ ForemanRhCloud.with_iop_smart_proxy? ? foreman_certificate : manifest_certificate
146
+ end
147
+
148
+ def manifest_certificate
149
+ candlepin_id_certificate = organization.owner_details['upstreamConsumer']['idCert']
150
+ {
151
+ cert: candlepin_id_certificate['cert'],
152
+ key: candlepin_id_certificate['key'],
153
+ }
154
+ end
155
+
156
+ def foreman_certificate
157
+ {
158
+ cert: File.read(Setting[:ssl_certificate]),
159
+ key: File.read(Setting[:ssl_priv_key]),
160
+ }
161
+ end
162
+
163
+ def filename
164
+ input[:filename]
165
+ end
166
+
167
+ def organization
168
+ Organization.find(input[:organization_id])
169
+ end
170
+
171
+ def content_disconnected?
172
+ !Setting[:subscription_connection_enabled]
173
+ end
174
+
175
+ def progress_output
176
+ progress_output = ProgressOutput.register(instance_label)
177
+ yield(progress_output)
178
+ ensure
179
+ progress_output.close
180
+ end
181
+
182
+ def instance_label
183
+ input[:instance_label]
184
+ end
185
+
186
+ def logger
187
+ Foreman::Logging.logger('background')
188
+ end
189
+
190
+ def rescue_strategy_for_self
191
+ Dynflow::Action::Rescue::Fail
192
+ end
193
+
194
+ def clear_task_output(label)
195
+ TaskOutputLine.where(label: label).delete_all
196
+ TaskOutputStatus.where(label: label).delete_all
197
+ end
198
+ end
199
+ end
200
+ end
@@ -48,10 +48,6 @@ module ForemanInventoryUpload
48
48
  @outputs_folder ||= ensure_folder(File.join(ForemanInventoryUpload.base_folder, 'outputs/'))
49
49
  end
50
50
 
51
- def self.upload_script_file
52
- 'uploader.sh'
53
- end
54
-
55
51
  def self.facts_archive_name(organization, filter = nil)
56
52
  "report_for_#{organization}#{filter.empty? ? nil : "[#{filter.to_s.parameterize}]"}.tar.xz"
57
53
  end
@@ -117,14 +117,17 @@ module ForemanRhCloud
117
117
  end
118
118
  end
119
119
 
120
+ # Preload insights facet to avoid N+1 queries when rendering host list with facets
121
+ add_controller_action_scope('Api::V2::HostsController', :index) do |base_scope|
122
+ base_scope.preload(:insights)
123
+ end
124
+
120
125
  register_global_js_file 'global'
121
126
 
122
127
  register_custom_status InventorySync::InventoryStatus
123
128
  register_custom_status InsightsClientReportStatus
124
- if ForemanRhCloud.with_iop_smart_proxy?
125
- register_ping_extension { ForemanRhCloud::Ping.ping }
126
- register_status_extension { ForemanRhCloud::Ping.status }
127
- end
129
+ register_ping_extension { ForemanRhCloud::Ping.ping }
130
+ register_status_extension { ForemanRhCloud::Ping.status }
128
131
 
129
132
  describe_host do
130
133
  overview_buttons_provider :insights_host_overview_buttons
@@ -146,6 +149,10 @@ module ForemanRhCloud
146
149
  end
147
150
  end
148
151
 
152
+ ::Foreman::Plugin.app_metadata_registry.register(:foreman_rh_cloud, {
153
+ iop: ForemanRhCloud.with_iop_smart_proxy?,
154
+ })
155
+
149
156
  extend_template_helpers ForemanRhCloud::TemplateRendererHelper
150
157
  allowed_template_helpers :remediations_playbook, :download_rh_playbook
151
158
  end
@@ -1,3 +1,3 @@
1
1
  module ForemanRhCloud
2
- VERSION = '12.2.13'.freeze
2
+ VERSION = '12.2.15'.freeze
3
3
  end
@@ -14,15 +14,15 @@ module ForemanRhCloud
14
14
 
15
15
  def self.base_url
16
16
  # for testing set ENV to 'https://ci.cloud.redhat.com'
17
- @base_url ||= env_or_on_premise_url('SATELLITE_RH_CLOUD_URL') || 'https://cloud.redhat.com'
17
+ env_or_on_premise_url('SATELLITE_RH_CLOUD_URL') || 'https://cloud.redhat.com'
18
18
  end
19
19
 
20
20
  def self.cert_base_url
21
- @cert_base_url ||= env_or_on_premise_url('SATELLITE_CERT_RH_CLOUD_URL') || 'https://cert.cloud.redhat.com'
21
+ env_or_on_premise_url('SATELLITE_CERT_RH_CLOUD_URL') || 'https://cert.cloud.redhat.com'
22
22
  end
23
23
 
24
24
  def self.legacy_insights_url
25
- @legacy_insights_url ||= env_or_on_premise_url('SATELLITE_LEGACY_INSIGHTS_URL') || 'https://cert-api.access.redhat.com'
25
+ env_or_on_premise_url('SATELLITE_LEGACY_INSIGHTS_URL') || 'https://cert-api.access.redhat.com'
26
26
  end
27
27
 
28
28
  def self.verify_ssl_method
@@ -126,10 +126,10 @@ module InsightsCloud
126
126
 
127
127
  def invocation_status
128
128
  hosts_state = Hash[job_invocation.targeting.hosts.map do |host|
129
- next unless host.insights&.uuid
129
+ next unless host.insights_uuid
130
130
  [
131
- host.insights.uuid,
132
- task_status(job_invocation.sub_task_for_host(host), host.insights.uuid),
131
+ host.insights_uuid,
132
+ task_status(job_invocation.sub_task_for_host(host), host.insights_uuid),
133
133
  ]
134
134
  end.compact]
135
135
 
@@ -8,8 +8,6 @@ module InventorySync
8
8
  set_callback :step, :around, :create_missing_hosts
9
9
 
10
10
  def plan(organizations)
11
- # Do not run for local advisor, since we use sub-man id to identify hosts.
12
- return if ForemanRhCloud.with_iop_smart_proxy?
13
11
  # by default the tasks will be executed concurrently
14
12
  super(organizations)
15
13
  plan_self_host_sync
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foreman_rh_cloud",
3
- "version": "12.2.13",
3
+ "version": "12.2.15",
4
4
  "description": "Inventory Upload =============",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -11,7 +11,7 @@ class AccountsControllerTest < ActionController::TestCase
11
11
  generate_label = ForemanInventoryUpload::Async::GenerateReportJob.output_label(test_org.id)
12
12
  generate_output = ForemanInventoryUpload::Async::ProgressOutput.register(generate_label)
13
13
  generate_output.status = 'generate_status_test'
14
- upload_label = ForemanInventoryUpload::Async::UploadReportJob.output_label(test_org.id)
14
+ upload_label = ForemanInventoryUpload::Async::UploadReportDirectJob.output_label(test_org.id)
15
15
  upload_output = ForemanInventoryUpload::Async::ProgressOutput.register(upload_label)
16
16
  upload_output.status = 'upload_status_test'
17
17
 
@@ -13,7 +13,7 @@ module InsightsCloud
13
13
  end
14
14
 
15
15
  test 'shows hosts with uuids' do
16
- uuids = [@host1.insights.uuid, @host2.insights.uuid]
16
+ uuids = [@host1.insights_uuid, @host2.insights_uuid]
17
17
  get :host_details, params: { organization_id: @test_org.id, host_uuids: uuids }
18
18
  assert_response :success
19
19
  assert_template 'api/v2/advisor_engine/host_details'
@@ -21,6 +21,33 @@ module InsightsCloud
21
21
  refute_equal @test_org.hosts.count, assigns(:hosts).count
22
22
  end
23
23
 
24
+ test 'in IoP mode host_details uses subscription uuid when insights uuid is stale' do
25
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
26
+
27
+ stale_insights_uuid = 'stale-insights-uuid-123'
28
+ subscription_uuid = 'subscription-uuid-456'
29
+
30
+ # Create host with diverging facet UUIDs
31
+ host = FactoryBot.create(:host, :with_subscription, organization: @test_org)
32
+ host.subscription_facet.update!(uuid: subscription_uuid)
33
+ host.insights = FactoryBot.create(:insights_facet, host_id: host.id, uuid: stale_insights_uuid)
34
+ host.save!
35
+
36
+ # Query using the stale insights UUID
37
+ get :host_details, params: {
38
+ organization_id: @test_org.id,
39
+ host_uuids: [stale_insights_uuid],
40
+ }
41
+
42
+ assert_response :success
43
+ body = JSON.parse(response.body)
44
+
45
+ # Should return the subscription UUID, not the stale insights UUID
46
+ insights_uuids = body.map { |h| h['insights_uuid'] }
47
+ assert_includes insights_uuids, subscription_uuid, "Should use subscription UUID in IoP mode"
48
+ refute_includes insights_uuids, stale_insights_uuid, "Should not use stale insights UUID"
49
+ end
50
+
24
51
  test 'shows error when no hosts found' do
25
52
  get :host_details, params: { organization_id: @test_org.id, host_uuids: ['nonexistentuuid'] }
26
53
  assert_response :not_found
@@ -180,6 +180,32 @@ module InsightsCloud
180
180
  assert_equal 'Cloud request failed', JSON.parse(@response.body)['message']
181
181
  assert_match(/#{@body}/, JSON.parse(@response.body)['response'])
182
182
  end
183
+
184
+ test "should allow forward_request with nil location (Any location)" do
185
+ net_http_resp = Net::HTTPResponse.new(1.0, 200, "OK")
186
+ res = RestClient::Response.create(@body, net_http_resp, @http_req)
187
+ ::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:forward_request).returns(res)
188
+
189
+ # Set session with nil location_id to simulate "Any location"
190
+ session_with_nil_location = set_session_user.merge(
191
+ organization_id: @org.id,
192
+ location_id: nil
193
+ )
194
+
195
+ get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: session_with_nil_location
196
+ assert_equal 200, @response.status
197
+ assert_equal @body, @response.body
198
+ end
199
+
200
+ test "should allow forward_request with location set" do
201
+ net_http_resp = Net::HTTPResponse.new(1.0, 200, "OK")
202
+ res = RestClient::Response.create(@body, net_http_resp, @http_req)
203
+ ::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:forward_request).returns(res)
204
+
205
+ get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
206
+ assert_equal 200, @response.status
207
+ assert_equal @body, @response.body
208
+ end
183
209
  end
184
210
 
185
211
  def set_session
@@ -8,7 +8,7 @@ class UploadsControllerTest < ActionController::TestCase
8
8
  test_org = FactoryBot.create(:organization)
9
9
  ForemanInventoryUpload::Async::ProgressOutput
10
10
  .expects(:get)
11
- .with(ForemanInventoryUpload::Async::UploadReportJob.output_label(test_org.id))
11
+ .with(ForemanInventoryUpload::Async::UploadReportDirectJob.output_label(test_org.id))
12
12
  .returns(progress_output)
13
13
  progress_output.expects(:full_output).returns('test output')
14
14
 
@@ -58,5 +58,34 @@ FactoryBot.modify do
58
58
  host.insights = FactoryBot.create(:insights_facet, :with_hits, host_id: host.id)
59
59
  end
60
60
  end
61
+
62
+ trait :with_mismatched_insights_uuid do
63
+ # Simulates host previously registered to cloud with stale UUID
64
+ # Requires :with_subscription trait to be used together
65
+ after(:create) do |host, _evaluator|
66
+ raise "subscription_facet required for :with_mismatched_insights_uuid trait" unless host.subscription_facet&.uuid
67
+
68
+ host.insights = FactoryBot.create(
69
+ :insights_facet,
70
+ host_id: host.id,
71
+ uuid: 'stale-cloud-uuid' # Different from subscription_facet
72
+ )
73
+ end
74
+ end
75
+
76
+ trait :with_synced_insights_uuid do
77
+ # Ideal state: insights_facet UUID matches subscription_facet UUID
78
+ # Requires :with_subscription trait to be used together
79
+ after(:create) do |host, _evaluator|
80
+ raise "subscription_facet required for :with_synced_insights_uuid trait" unless host.subscription_facet&.uuid
81
+
82
+ subscription_uuid = host.subscription_facet.uuid
83
+ host.insights = FactoryBot.create(
84
+ :insights_facet,
85
+ host_id: host.id,
86
+ uuid: subscription_uuid # Same as subscription_facet
87
+ )
88
+ end
89
+ end
61
90
  end
62
91
  end