foreman_rh_cloud 13.0.8 → 13.0.9

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/foreman_inventory_upload/accounts_controller.rb +1 -1
  3. data/app/controllers/foreman_inventory_upload/uploads_controller.rb +1 -1
  4. data/lib/foreman_inventory_upload/async/queue_for_upload_job.rb +1 -23
  5. data/lib/foreman_inventory_upload/async/upload_report_direct_job.rb +200 -0
  6. data/lib/foreman_inventory_upload.rb +0 -4
  7. data/lib/foreman_rh_cloud/version.rb +1 -1
  8. data/lib/inventory_sync/async/inventory_hosts_sync.rb +0 -2
  9. data/package.json +1 -1
  10. data/test/controllers/accounts_controller_test.rb +1 -1
  11. data/test/controllers/uploads_controller_test.rb +1 -1
  12. data/test/jobs/queue_for_upload_job_test.rb +1 -12
  13. data/test/jobs/upload_report_direct_job_test.rb +399 -0
  14. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.js +1 -1
  15. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.fixtures.js +4 -5
  16. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.js +4 -2
  17. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/__snapshots__/ListItem.test.js.snap +9 -10
  18. data/webpack/ForemanInventoryUpload/Components/Dashboard/Dashboard.js +4 -1
  19. data/webpack/ForemanInventoryUpload/Components/PageHeader/PageHeader.js +24 -17
  20. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageHeader.test.js +178 -8
  21. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/ToolbarButtons.js +3 -1
  22. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/__tests__/ToolbarButtons.test.js +69 -51
  23. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTable.test.js +11 -19
  24. data/webpack/InsightsVulnerabilityHostIndexExtensions/__tests__/CVECountCell.test.js +77 -22
  25. metadata +4 -8
  26. data/lib/foreman_inventory_upload/async/upload_report_job.rb +0 -97
  27. data/lib/foreman_inventory_upload/scripts/uploader.sh.erb +0 -55
  28. data/test/jobs/upload_report_job_test.rb +0 -38
  29. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageHeader.test.js.snap +0 -36
  30. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTable.test.js.snap +0 -112
  31. 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: aba47c451526fd85b565ba8fdcb17d6b7d643cd3c6fbea186a40943a6a4887bc
4
- data.tar.gz: 16357a50048143a026497016cd38533b34d211c6ea61fccf55bd2a298852766e
3
+ metadata.gz: 9d818ad6b4142546fe8d3b8c0e54f49600ed30915f6b22f142bc1861938c5dba
4
+ data.tar.gz: 3d0e310b2cf587243c64623713af21e164c87e87e21a5edeee556f16613ae8fc
5
5
  SHA512:
6
- metadata.gz: c863fed43a17edb0cfdb465e4b26b529e9a09460ab9677cea6e94bc3fea6c560c8fdf01c691ea4ea2d495c7ab715db90cbee5c3bf87217e3aae135c9b29b5e2f
7
- data.tar.gz: c1d61f3f092e87829a28fd1ac237f46c29772aeae926d2a3c65cba440d3233844aa8dd40eb38a18ff56a09c68f9578aaf83a142d37d1aff06679d0ba6b87c904
6
+ metadata.gz: ae35c83c6c45af99a891a3ff243d10f7f7616891facecab5f6afa34287f2282eb3c0175680537bb0a17b62352ea42b43d1e1d4365a517f6086c3e1d82fbd725b
7
+ data.tar.gz: f9e14d6356e65f5649b3c4a2f57ef381539aa296e12fbbe7c0e5fcc28c47e855bd9ad8e1ab3a9ca739319b9c9882e41de9d578244be2f1fcbd83ca8d1c4160de
@@ -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: {
@@ -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
@@ -1,3 +1,3 @@
1
1
  module ForemanRhCloud
2
- VERSION = '13.0.8'.freeze
2
+ VERSION = '13.0.9'.freeze
3
3
  end
@@ -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": "13.0.8",
3
+ "version": "13.0.9",
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
 
@@ -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
 
@@ -11,13 +11,6 @@ class QueueForUploadJobTest < ActiveSupport::TestCase
11
11
  let(:uploads_folder) { ForemanInventoryUpload.uploads_folder }
12
12
 
13
13
  setup do
14
- # Stub the script template source
15
- script_source = File.join(ForemanRhCloud::Engine.root, 'lib/foreman_inventory_upload/scripts/uploader.sh.erb')
16
- File.stubs(:read).with(script_source).returns('#!/bin/bash\necho "Test script"')
17
-
18
- # Stub template rendering
19
- Foreman::Renderer.stubs(:render).returns('#!/bin/bash\necho "Rendered script"')
20
-
21
14
  # Stub additional settings that are accessed
22
15
  Setting.stubs(:[]).with(:content_default_http_proxy).returns(nil)
23
16
  Setting.stubs(:[]).with(:http_proxy).returns(nil)
@@ -49,7 +42,7 @@ class QueueForUploadJobTest < ActiveSupport::TestCase
49
42
  assert File.exist?(File.join(uploads_folder, report_file)), "File should exist in uploads folder"
50
43
  end
51
44
 
52
- test 'creates necessary folders and scripts' do
45
+ test 'creates necessary folders' do
53
46
  ForemanInventoryUpload::Async::QueueForUploadJob.any_instance.stubs(:plan_upload_report)
54
47
 
55
48
  action = create_and_plan_action(ForemanInventoryUpload::Async::QueueForUploadJob, base_folder, report_file, organization.id)
@@ -57,9 +50,5 @@ class QueueForUploadJobTest < ActiveSupport::TestCase
57
50
 
58
51
  # Verify the uploads folder was created
59
52
  assert Dir.exist?(uploads_folder), "Uploads folder should be created"
60
-
61
- # Verify the script file was created
62
- script_path = File.join(uploads_folder, ForemanInventoryUpload.upload_script_file)
63
- assert File.exist?(script_path), "Upload script should be created"
64
53
  end
65
54
  end