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.
- checksums.yaml +4 -4
- data/app/controllers/foreman_inventory_upload/accounts_controller.rb +1 -1
- data/app/controllers/foreman_inventory_upload/uploads_controller.rb +1 -1
- data/lib/foreman_inventory_upload/async/queue_for_upload_job.rb +1 -23
- data/lib/foreman_inventory_upload/async/upload_report_direct_job.rb +200 -0
- data/lib/foreman_inventory_upload.rb +0 -4
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/lib/inventory_sync/async/inventory_hosts_sync.rb +0 -2
- data/package.json +1 -1
- data/test/controllers/accounts_controller_test.rb +1 -1
- data/test/controllers/uploads_controller_test.rb +1 -1
- data/test/jobs/queue_for_upload_job_test.rb +1 -12
- data/test/jobs/upload_report_direct_job_test.rb +399 -0
- data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.js +1 -1
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.fixtures.js +4 -5
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.js +4 -2
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/__snapshots__/ListItem.test.js.snap +9 -10
- data/webpack/ForemanInventoryUpload/Components/Dashboard/Dashboard.js +4 -1
- data/webpack/ForemanInventoryUpload/Components/PageHeader/PageHeader.js +24 -17
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageHeader.test.js +178 -8
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/ToolbarButtons.js +3 -1
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/__tests__/ToolbarButtons.test.js +69 -51
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTable.test.js +11 -19
- data/webpack/InsightsVulnerabilityHostIndexExtensions/__tests__/CVECountCell.test.js +77 -22
- metadata +4 -8
- data/lib/foreman_inventory_upload/async/upload_report_job.rb +0 -97
- data/lib/foreman_inventory_upload/scripts/uploader.sh.erb +0 -55
- data/test/jobs/upload_report_job_test.rb +0 -38
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageHeader.test.js.snap +0 -36
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTable.test.js.snap +0 -112
- 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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9d818ad6b4142546fe8d3b8c0e54f49600ed30915f6b22f142bc1861938c5dba
|
|
4
|
+
data.tar.gz: 3d0e310b2cf587243c64623713af21e164c87e87e21a5edeee556f16613ae8fc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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::
|
|
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::
|
|
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(
|
|
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
|
|
@@ -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
|
@@ -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::
|
|
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::
|
|
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
|
|
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
|