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.
- checksums.yaml +4 -4
- data/app/controllers/concerns/foreman_rh_cloud/registration_manager_extensions.rb +2 -2
- data/app/controllers/concerns/insights_cloud/package_profile_upload_extensions.rb +3 -0
- data/app/controllers/foreman_inventory_upload/accounts_controller.rb +1 -1
- data/app/controllers/foreman_inventory_upload/uploads_controller.rb +1 -1
- data/app/controllers/insights_cloud/ui_requests_controller.rb +2 -3
- data/app/models/concerns/rh_cloud_host.rb +14 -0
- data/app/models/foreman_rh_cloud/ping.rb +2 -1
- data/app/services/foreman_rh_cloud/tags_auth.rb +13 -3
- data/app/views/api/v2/advisor_engine/host_details.json.rabl +1 -3
- data/app/views/api/v2/hosts/insights/base.rabl +3 -2
- 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/plugin.rb +11 -4
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/lib/foreman_rh_cloud.rb +3 -3
- data/lib/insights_cloud/async/connector_playbook_execution_reporter_task.rb +3 -3
- 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/insights_cloud/api/advisor_engine_controller_test.rb +28 -1
- data/test/controllers/insights_cloud/ui_requests_controller_test.rb +26 -0
- data/test/controllers/uploads_controller_test.rb +1 -1
- data/test/factories/insights_factories.rb +29 -0
- data/test/jobs/queue_for_upload_job_test.rb +1 -12
- data/test/jobs/upload_report_direct_job_test.rb +399 -0
- data/test/unit/foreman_rh_cloud_iop_metadata_test.rb +200 -0
- data/test/unit/lib/foreman_rh_cloud/registration_manager_extensions_test.rb +4 -42
- data/test/unit/rh_cloud_host_test.rb +191 -0
- data/test/unit/services/foreman_rh_cloud/tags_auth_test.rb +14 -0
- data/webpack/ForemanColumnExtensions/index.js +2 -0
- 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/__tests__/__snapshots__/PageTitle.test.js.snap +1 -1
- 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/ForemanInventoryUpload/ForemanInventoryHelpers.js +1 -1
- data/webpack/ForemanInventoryUpload/__tests__/__snapshots__/ForemanInventoryHelpers.test.js.snap +1 -1
- data/webpack/InsightsCloudSync/Components/InsightsSettings/InsightsSettings.js +3 -3
- data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTable.js +3 -9
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTable.test.js +24 -4
- data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.js +2 -2
- data/webpack/InsightsCloudSync/Components/ToolbarDropdown.js +3 -3
- data/webpack/InsightsCloudSync/InsightsCloudSync.js +3 -3
- data/webpack/InsightsCloudSync/InsightsCloudSync.test.js +10 -0
- data/webpack/InsightsCloudSync/__snapshots__/InsightsCloudSync.test.js.snap +1 -1
- data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +5 -5
- data/webpack/InsightsVulnerabilityHostIndexExtensions/CVECountCell.js +2 -2
- data/webpack/InsightsVulnerabilityHostIndexExtensions/__tests__/CVECountCell.test.js +77 -22
- data/webpack/common/Hooks/ConfigHooks.js +3 -16
- metadata +9 -9
- 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: 298be7d24bfc6e80e79eee94df423d76892fcf4f45a8750b940776f0647e4761
|
|
4
|
+
data.tar.gz: 6c3f86b461e2697775133e0c9483eefc09b8ae35fc7912810f80f43a66f78512
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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.
|
|
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::
|
|
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: {
|
|
@@ -2,7 +2,7 @@ module InsightsCloud
|
|
|
2
2
|
class UIRequestsController < ::ApplicationController
|
|
3
3
|
layout false
|
|
4
4
|
|
|
5
|
-
before_action :ensure_org, :
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
"
|
|
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
|
|
@@ -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
|
|
@@ -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
|
-
|
|
125
|
-
|
|
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
|
data/lib/foreman_rh_cloud.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
129
|
+
next unless host.insights_uuid
|
|
130
130
|
[
|
|
131
|
-
host.
|
|
132
|
-
task_status(job_invocation.sub_task_for_host(host), host.
|
|
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
|
@@ -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
|
|
|
@@ -13,7 +13,7 @@ module InsightsCloud
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
test 'shows hosts with uuids' do
|
|
16
|
-
uuids = [@host1.
|
|
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::
|
|
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
|