foreman_rh_cloud 13.2.9 → 13.2.11
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/insights_cloud/api/machine_telemetries_controller.rb +4 -8
- data/app/controllers/insights_cloud/ui_requests_controller.rb +3 -7
- data/app/models/concerns/rh_cloud_host.rb +36 -1
- data/app/services/foreman_rh_cloud/url_remediations_retriever.rb +19 -5
- data/lib/foreman_inventory_upload/async/upload_report_direct_job.rb +2 -0
- data/lib/foreman_inventory_upload/generators/fact_helpers.rb +26 -4
- data/lib/foreman_inventory_upload.rb +8 -1
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/lib/foreman_rh_cloud.rb +36 -9
- data/lib/insights_cloud/async/insights_generate_notifications.rb +10 -1
- data/lib/inventory_sync/async/inventory_self_host_sync.rb +12 -2
- data/package.json +1 -1
- data/test/controllers/insights_cloud/api/machine_telemetries_controller_test.rb +16 -2
- data/test/controllers/insights_cloud/ui_requests_controller_test.rb +16 -2
- data/test/jobs/insights_generate_notifications_test.rb +26 -0
- data/test/jobs/inventory_self_host_sync_test.rb +9 -0
- data/test/jobs/upload_report_direct_job_test.rb +29 -0
- data/test/unit/foreman_rh_cloud_self_host_test.rb +50 -2
- data/test/unit/insights_facet_test.rb +5 -0
- data/test/unit/metadata_generator_test.rb +24 -1
- data/test/unit/rh_cloud_host_test.rb +68 -0
- data/test/unit/services/foreman_rh_cloud/url_remediations_retriever_test.rb +82 -5
- data/webpack/CVEsHostDetailsTab/CVEsHostDetailsTab.js +24 -2
- data/webpack/CVEsHostDetailsTab/__tests__/CVEsHostDetailsTab.test.js +73 -10
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyResults/__tests__/EmptyResults.test.js +10 -9
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyState/__tests__/EmptyState.test.js +13 -9
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ErrorState/__tests__/ErrorState.test.js +20 -9
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/ListItem.test.js +31 -8
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/__tests__/ListItemStatus.test.js +26 -10
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountList.test.js +33 -9
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListReducer.test.js +55 -35
- data/webpack/ForemanInventoryUpload/Components/FileDownload/__tests__/FileDownload.test.js +13 -9
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/InventoryFilter.test.js +12 -15
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/integration.test.js +32 -12
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageTitle.test.js +15 -7
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/__tests__/CloudConnectorButton.test.js +47 -18
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SettingsWarning/SettingsWarning.test.js +58 -15
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/SyncButton.test.js +23 -9
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/SyncButtonSelectors.test.js +19 -17
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/integrations.test.js +25 -37
- data/webpack/ForemanInventoryUpload/Components/ScheduledRun/__tests__/ScheduledRun.test.js +28 -8
- data/webpack/ForemanInventoryUpload/Components/StatusChart/__tests__/StatusChart.test.js +25 -8
- data/webpack/ForemanInventoryUpload/Components/TabContainer/__tests__/TabContainer.test.js +11 -9
- data/webpack/ForemanInventoryUpload/Components/TabFooter/__tests__/TabFooter.test.js +11 -9
- data/webpack/ForemanInventoryUpload/ForemanInventoryHelpers.js +4 -4
- data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/__tests__/InventoryAutoUpload.test.js +33 -12
- data/webpack/ForemanInventoryUpload/__tests__/ForemanInventoryHelpers.test.js +36 -8
- data/webpack/ForemanRhCloudHelpers.js +22 -0
- data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +27 -1
- data/webpack/InsightsHostDetailsTab/__tests__/NewHostDetailsTab.test.js +134 -22
- metadata +10 -43
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyResults/__tests__/__snapshots__/EmptyResults.test.js.snap +0 -18
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyState/__tests__/__snapshots__/EmptyState.test.js.snap +0 -25
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ErrorState/__tests__/__snapshots__/ErrorState.test.js.snap +0 -20
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/__snapshots__/ListItem.test.js.snap +0 -47
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/__tests__/__snapshots__/ListItemStatus.test.js.snap +0 -59
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListActions.test.js +0 -34
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListIntegration.test.js +0 -14
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListSelectors.test.js +0 -25
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountList.test.js.snap +0 -49
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListActions.test.js.snap +0 -86
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListReducer.test.js.snap +0 -75
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListSelectors.test.js.snap +0 -46
- data/webpack/ForemanInventoryUpload/Components/FileDownload/__tests__/__snapshots__/FileDownload.test.js.snap +0 -26
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/InventoryFilterActions.test.js +0 -14
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/InventoryFilterReducer.test.js +0 -28
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/InventoryFilterSelectors.test.js +0 -21
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/InventoryFilter.test.js.snap +0 -21
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/InventoryFilterActions.test.js.snap +0 -17
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/InventoryFilterReducer.test.js.snap +0 -19
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/InventoryFilterSelectors.test.js.snap +0 -9
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/integration.test.js.snap +0 -43
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/AdvancedSetting/__tests__/AdvancedSettingActions.test.js +0 -9
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/AdvancedSetting/__tests__/__snapshots__/AdvancedSettingActions.test.js.snap +0 -18
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/__tests__/InventorySettingsActions.test.js +0 -14
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/__tests__/__snapshots__/InventorySettingsActions.test.js.snap +0 -26
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageTitle.test.js.snap +0 -68
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/__tests__/CloudConnectorActions.test.js +0 -9
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/__tests__/__snapshots__/CloudConnectorActions.test.js.snap +0 -11
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/__tests__/__snapshots__/CloudConnectorButton.test.js.snap +0 -59
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SettingsWarning/__snapshots__/SettingsWarning.test.js.snap +0 -32
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/SyncButton.test.js.snap +0 -15
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/SyncButtonSelectors.test.js.snap +0 -3
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/integrations.test.js.snap +0 -58
- data/webpack/ForemanInventoryUpload/Components/ScheduledRun/__tests__/__snapshots__/ScheduledRun.test.js.snap +0 -23
- data/webpack/ForemanInventoryUpload/Components/StatusChart/__tests__/__snapshots__/StatusChart.test.js.snap +0 -74
- data/webpack/ForemanInventoryUpload/Components/TabContainer/__tests__/__snapshots__/TabContainer.test.js.snap +0 -18
- data/webpack/ForemanInventoryUpload/Components/TabFooter/__tests__/__snapshots__/TabFooter.test.js.snap +0 -12
- data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/__tests__/__snapshots__/InventoryAutoUpload.test.js.snap +0 -96
- data/webpack/ForemanInventoryUpload/__tests__/ForemanInventoryUpload.test.js +0 -10
- data/webpack/ForemanInventoryUpload/__tests__/__snapshots__/ForemanInventoryHelpers.test.js.snap +0 -5
- data/webpack/ForemanInventoryUpload/__tests__/__snapshots__/ForemanInventoryUpload.test.js.snap +0 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2139dff816242e974e133f964b393409a65962f4dc5a5de0651b85d1d3c4bcb7
|
|
4
|
+
data.tar.gz: f362df63107b1a8834c6984654bedef584875e6d1d226dcab130c1d8fa11d181
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1a06fbaad9e79093b83f8ac8454ffcbf0ffa875f1978ef04697b8cfcf58c0a35812a7eee649f08b45df4c938a20e6784bffcba5bbc702c73a5bf224fe8958b47
|
|
7
|
+
data.tar.gz: d5c4b920bc78b449ddca4b080be077a24c3766950de46f30f9611745a85927e37e44c18d7aea6223b91e2faa4e3a6eb39fe9fab139207d9c73a1917c6910b0cd
|
|
@@ -32,14 +32,10 @@ module InsightsCloud::Api
|
|
|
32
32
|
rescue RestClient::ExceptionWithResponse => e
|
|
33
33
|
response_obj = e.response.presence || e.exception
|
|
34
34
|
code = response_obj.try(:code) || response_obj.try(:http_code) || 500
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
:error => response_obj.to_s,
|
|
40
|
-
:headers => {},
|
|
41
|
-
:response => response_obj,
|
|
42
|
-
}, status: code
|
|
35
|
+
upstream_content_type = response_obj.try(:headers)&.[](:content_type)
|
|
36
|
+
content_type = upstream_content_type&.match?(/json/) ? upstream_content_type : 'application/json'
|
|
37
|
+
|
|
38
|
+
return render body: response_obj.to_s, status: code, content_type: content_type
|
|
43
39
|
rescue StandardError => e
|
|
44
40
|
# Catch any other exceptions here, such as Errno::ECONNREFUSED
|
|
45
41
|
logger.warn("Cloud request failed with exception: #{e}")
|
|
@@ -36,14 +36,10 @@ module InsightsCloud
|
|
|
36
36
|
rescue RestClient::ExceptionWithResponse => e
|
|
37
37
|
response_obj = e.response.presence || e.exception
|
|
38
38
|
code = response_obj.try(:code) || response_obj.try(:http_code) || 500
|
|
39
|
-
|
|
39
|
+
upstream_content_type = response_obj.try(:headers)&.[](:content_type)
|
|
40
|
+
content_type = upstream_content_type&.match?(/json/) ? upstream_content_type : 'application/json'
|
|
40
41
|
|
|
41
|
-
return render
|
|
42
|
-
:message => message,
|
|
43
|
-
:error => response_obj.to_s,
|
|
44
|
-
:headers => {},
|
|
45
|
-
:response => response_obj,
|
|
46
|
-
}, status: code
|
|
42
|
+
return render body: response_obj.to_s, status: code, content_type: content_type
|
|
47
43
|
rescue StandardError => e
|
|
48
44
|
# Catch any other exceptions here, such as Errno::ECONNREFUSED
|
|
49
45
|
logger.warn("Cloud request failed with exception: #{e}")
|
|
@@ -10,7 +10,8 @@ module RhCloudHost
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
has_many :insights_hits, through: :insights, source: :hits
|
|
13
|
-
scoped_search :
|
|
13
|
+
scoped_search :on => :id, :rename => :insights_recommendations_count, :only_explicit => true,
|
|
14
|
+
:ext_method => :search_by_insights_recommendations_count, :complete_value => false
|
|
14
15
|
|
|
15
16
|
has_one :insights_client_report_status_object, :class_name => '::InsightsClientReportStatus', :foreign_key => 'host_id'
|
|
16
17
|
scoped_search :relation => :insights_client_report_status_object, :on => :status, :rename => :insights_client_report_status,
|
|
@@ -46,6 +47,40 @@ module RhCloudHost
|
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
module ClassMethods
|
|
50
|
+
def search_by_insights_recommendations_count(_key, operator, value)
|
|
51
|
+
if ForemanRhCloud.with_iop_smart_proxy?
|
|
52
|
+
raise ScopedSearch::QueryNotSupported.new(
|
|
53
|
+
_('Searching by recommendations count is not available in IoP mode.')
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
unless ScopedSearch::QueryBuilder::SQL_OPERATORS.value?(operator)
|
|
58
|
+
raise ScopedSearch::QueryNotSupported.new(
|
|
59
|
+
_('Unsupported operator for recommendations count search: %s') % operator
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
facet_table = InsightsFacet.table_name
|
|
64
|
+
hits_count_col = "COALESCE(#{facet_table}.hits_count, 0)"
|
|
65
|
+
|
|
66
|
+
if ['IN', 'NOT IN'].include?(operator)
|
|
67
|
+
values = value.is_a?(Array) ? value : value.to_s.split(',').map(&:strip)
|
|
68
|
+
placeholders = (['?'] * values.size).join(',')
|
|
69
|
+
condition = sanitize_sql_for_conditions(
|
|
70
|
+
["#{hits_count_col} #{operator} (#{placeholders})", *values]
|
|
71
|
+
)
|
|
72
|
+
else
|
|
73
|
+
condition = sanitize_sql_for_conditions(
|
|
74
|
+
["#{hits_count_col} #{operator} ?", value_to_sql(operator, value)]
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
{
|
|
79
|
+
joins: "LEFT JOIN #{facet_table} ON #{facet_table}.host_id = #{Host::Managed.table_name}.id",
|
|
80
|
+
conditions: condition,
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
|
|
49
84
|
def search_by_insights_uuid(_key, operator, value)
|
|
50
85
|
# Determine which facet table to search based on IoP mode
|
|
51
86
|
facet_table = ForemanRhCloud.with_iop_smart_proxy? ? Katello::Host::SubscriptionFacet.table_name : InsightsFacet.table_name
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
module ForemanRhCloud
|
|
2
2
|
class URLRemediationsRetriever < RemediationsRetriever
|
|
3
|
-
attr_reader :url, :
|
|
3
|
+
attr_reader :url, :headers
|
|
4
4
|
|
|
5
5
|
def initialize(url:, organization_id:, payload: '', headers: {}, logger: Logger.new(IO::NULL))
|
|
6
6
|
super(logger: logger)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
parsed_url = URI.parse(url)
|
|
9
|
+
query_params = parsed_url.query ? CGI.parse(parsed_url.query) : {}
|
|
10
|
+
hosts_param = query_params.delete('hosts')
|
|
11
|
+
|
|
12
|
+
if hosts_param.present?
|
|
13
|
+
@host_uuids = hosts_param.flat_map { |v| v.split(',') }.map(&:strip).reject(&:blank?)
|
|
14
|
+
@host_uuids = nil if @host_uuids.empty?
|
|
15
|
+
parsed_url.query = query_params.any? ? URI.encode_www_form(query_params) : nil
|
|
16
|
+
@url = parsed_url.to_s
|
|
17
|
+
else
|
|
18
|
+
@host_uuids = nil
|
|
19
|
+
@url = url
|
|
20
|
+
end
|
|
21
|
+
|
|
9
22
|
@payload = payload
|
|
10
23
|
@headers = headers
|
|
11
24
|
@organization_id = organization_id
|
|
@@ -14,7 +27,7 @@ module ForemanRhCloud
|
|
|
14
27
|
private
|
|
15
28
|
|
|
16
29
|
def query_playbook
|
|
17
|
-
logger.debug("Querying playbook at: #{url} with payload: #{payload} and headers: #{headers}")
|
|
30
|
+
logger.debug("Querying playbook via #{method.to_s.upcase} at: #{url} with payload: #{payload} and headers: #{headers}")
|
|
18
31
|
|
|
19
32
|
super
|
|
20
33
|
end
|
|
@@ -28,11 +41,12 @@ module ForemanRhCloud
|
|
|
28
41
|
end
|
|
29
42
|
|
|
30
43
|
def payload
|
|
31
|
-
|
|
44
|
+
return @host_uuids.to_json if @host_uuids.present?
|
|
45
|
+
@payload.present? ? @payload.to_json : @payload
|
|
32
46
|
end
|
|
33
47
|
|
|
34
48
|
def method
|
|
35
|
-
:get
|
|
49
|
+
@host_uuids.present? ? :post : :get
|
|
36
50
|
end
|
|
37
51
|
|
|
38
52
|
def organization
|
|
@@ -59,11 +59,13 @@ module ForemanInventoryUpload
|
|
|
59
59
|
def try_execute
|
|
60
60
|
if content_disconnected?
|
|
61
61
|
logger.info("Upload canceled: connection to Insights is not enabled. Report location: #{filename}")
|
|
62
|
+
done!
|
|
62
63
|
return
|
|
63
64
|
end
|
|
64
65
|
|
|
65
66
|
unless organization.owner_details&.dig('upstreamConsumer', 'idCert')
|
|
66
67
|
logger.info("Skipping organization '#{organization}', no candlepin certificate defined.")
|
|
68
|
+
done!
|
|
67
69
|
return
|
|
68
70
|
end
|
|
69
71
|
|
|
@@ -114,6 +114,7 @@ module ForemanInventoryUpload
|
|
|
114
114
|
|
|
115
115
|
def host_ips(host)
|
|
116
116
|
# Determines and returns the IP addresses associated with a host, applying obfuscation if enabled.
|
|
117
|
+
return {} if host.nil?
|
|
117
118
|
|
|
118
119
|
# If IP obfuscation is enabled for the host return a representation of obfuscated IP addresses.
|
|
119
120
|
return obfuscated_ips(host) if obfuscate_ips?(host)
|
|
@@ -163,10 +164,29 @@ module ForemanInventoryUpload
|
|
|
163
164
|
|
|
164
165
|
def hostname_match
|
|
165
166
|
bash_hostname = `uname -n`.chomp
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
foreman_host = ForemanRhCloud.foreman_host
|
|
168
|
+
|
|
169
|
+
# If bash hostname matches foreman_host, use fqdn
|
|
170
|
+
if foreman_host && bash_hostname == foreman_host.name
|
|
171
|
+
return fqdn(foreman_host)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# If no foreman_host, try foreman_host_name from Setting[:foreman_url]
|
|
175
|
+
unless foreman_host
|
|
176
|
+
foreman_hostname_from_setting = ForemanRhCloud.foreman_host_name
|
|
177
|
+
if foreman_hostname_from_setting
|
|
178
|
+
# Apply obfuscation if enabled
|
|
179
|
+
return obfuscate_fqdn(foreman_hostname_from_setting) if Setting[:obfuscate_inventory_hostnames]
|
|
180
|
+
|
|
181
|
+
return foreman_hostname_from_setting
|
|
182
|
+
end
|
|
183
|
+
# Otherwise fall through to bash_hostname below
|
|
184
|
+
# NOTE: Containerized foremanctl setups must configure Setting[:foreman_url]
|
|
185
|
+
# as bash hostname may not be available or meaningful in containers
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Fallback to bash hostname (with obfuscation if enabled)
|
|
189
|
+
if Setting[:obfuscate_inventory_hostnames]
|
|
170
190
|
obfuscate_fqdn(bash_hostname)
|
|
171
191
|
else
|
|
172
192
|
bash_hostname
|
|
@@ -174,6 +194,8 @@ module ForemanInventoryUpload
|
|
|
174
194
|
end
|
|
175
195
|
|
|
176
196
|
def bios_uuid(host)
|
|
197
|
+
return nil if host.nil?
|
|
198
|
+
|
|
177
199
|
value = fact_value(host, 'dmi::system::uuid') || ''
|
|
178
200
|
uuid_value(value)
|
|
179
201
|
end
|
|
@@ -96,7 +96,14 @@ module ForemanInventoryUpload
|
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
def self.inventory_self_url
|
|
99
|
-
|
|
99
|
+
host = ForemanRhCloud.foreman_host
|
|
100
|
+
hostname = host ? host.fqdn : ForemanRhCloud.foreman_host_name
|
|
101
|
+
if hostname.nil?
|
|
102
|
+
Rails.logger.warn("Cannot determine Foreman hostname for inventory sync. " \
|
|
103
|
+
"Please configure Setting[:foreman_url]. " \
|
|
104
|
+
"Containerized setups must explicitly set this.")
|
|
105
|
+
end
|
|
106
|
+
inventory_base_url + "?hostname_or_id=#{hostname}"
|
|
100
107
|
end
|
|
101
108
|
|
|
102
109
|
def self.host_by_id_url(host_uuid)
|
data/lib/foreman_rh_cloud.rb
CHANGED
|
@@ -84,23 +84,50 @@ module ForemanRhCloud
|
|
|
84
84
|
|
|
85
85
|
# For testing purposes we can override the default hostname with an environment variable SATELLITE_RH_CLOUD_FOREMAN_HOST
|
|
86
86
|
def self.foreman_host
|
|
87
|
-
@foreman_host
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
return @foreman_host if defined?(@foreman_host)
|
|
88
|
+
|
|
89
|
+
fullname = foreman_host_name
|
|
90
|
+
return @foreman_host = nil unless fullname
|
|
91
|
+
|
|
92
|
+
# Try fullname first
|
|
93
|
+
host = ::Host.unscoped.friendly.where(name: fullname).first
|
|
94
|
+
|
|
95
|
+
# If not found, try shortname
|
|
96
|
+
if host.nil?
|
|
92
97
|
shortname = /(?<shortname>[^\.]*)\.?.*/.match(fullname)[:shortname]
|
|
93
|
-
::Host.unscoped.friendly.
|
|
98
|
+
host = ::Host.unscoped.friendly.where(name: shortname).first
|
|
94
99
|
end
|
|
100
|
+
|
|
101
|
+
@foreman_host = host
|
|
95
102
|
end
|
|
96
103
|
|
|
97
104
|
def self.foreman_host_name
|
|
98
|
-
ENV['SATELLITE_RH_CLOUD_FOREMAN_HOST'] || marked_foreman_host&.name ||
|
|
105
|
+
ENV['SATELLITE_RH_CLOUD_FOREMAN_HOST'] || marked_foreman_host&.name || foreman_url_hostname
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def self.foreman_url_hostname
|
|
109
|
+
return nil unless Setting[:foreman_url]
|
|
110
|
+
|
|
111
|
+
begin
|
|
112
|
+
# Ensure setting is a string to avoid TypeError from URI.parse
|
|
113
|
+
url = Setting[:foreman_url].to_s
|
|
114
|
+
URI.parse(url).host
|
|
115
|
+
rescue URI::InvalidURIError, ArgumentError, TypeError => e
|
|
116
|
+
Rails.logger.warn("Invalid foreman_url setting: #{e.message}")
|
|
117
|
+
nil
|
|
118
|
+
end
|
|
99
119
|
end
|
|
100
120
|
|
|
101
121
|
def self.marked_foreman_host
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
# Find host with infrastructure_facet.foreman_instance = true
|
|
123
|
+
# Facets use a special mechanism in Foreman, so we query the facet table directly
|
|
124
|
+
return nil unless defined?(HostFacets::InfrastructureFacet)
|
|
125
|
+
|
|
126
|
+
facet = HostFacets::InfrastructureFacet.find_by(foreman_instance: true)
|
|
127
|
+
facet&.host
|
|
128
|
+
rescue ActiveRecord::StatementInvalid => e
|
|
129
|
+
# Table might not exist yet during migrations
|
|
130
|
+
Rails.logger.debug("Could not query marked foreman host: #{e.message}")
|
|
104
131
|
nil
|
|
105
132
|
end
|
|
106
133
|
|
|
@@ -13,7 +13,16 @@ module InsightsCloud
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def add_satellite_notifications
|
|
16
|
-
|
|
16
|
+
host = foreman_host
|
|
17
|
+
|
|
18
|
+
# Skip if no Foreman host record exists
|
|
19
|
+
unless host
|
|
20
|
+
logger.debug("Skipping Insights notifications: no Foreman host record found")
|
|
21
|
+
blueprint&.notifications&.destroy_all
|
|
22
|
+
return
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
hits_count = InsightsHit.where(host_id: host.id).count
|
|
17
26
|
|
|
18
27
|
# Remove stale notifications
|
|
19
28
|
blueprint.notifications.destroy_all
|
|
@@ -4,7 +4,14 @@ module InventorySync
|
|
|
4
4
|
set_callback :step, :around, :create_facets
|
|
5
5
|
|
|
6
6
|
def plan
|
|
7
|
-
|
|
7
|
+
host = ForemanRhCloud.foreman_host
|
|
8
|
+
|
|
9
|
+
if host.nil?
|
|
10
|
+
logger.warn("Skipping self-host inventory sync: no Foreman host record found.")
|
|
11
|
+
return
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
super(host.organization)
|
|
8
15
|
end
|
|
9
16
|
|
|
10
17
|
def create_facets
|
|
@@ -22,7 +29,10 @@ module InventorySync
|
|
|
22
29
|
private
|
|
23
30
|
|
|
24
31
|
def add_missing_insights_facet(uuids_hash)
|
|
25
|
-
|
|
32
|
+
host = ForemanRhCloud.foreman_host
|
|
33
|
+
return unless host # Guard against nil
|
|
34
|
+
|
|
35
|
+
facet = InsightsFacet.find_or_create_by(host_id: host.id) do |facet|
|
|
26
36
|
facet.uuid = uuids_hash.values.first
|
|
27
37
|
end
|
|
28
38
|
|
data/package.json
CHANGED
|
@@ -154,8 +154,22 @@ module InsightsCloud::Api
|
|
|
154
154
|
|
|
155
155
|
get :forward_request, params: { "path" => "platform/module-update-router/v1/channel" }
|
|
156
156
|
assert_equal 500, @response.status
|
|
157
|
-
assert_equal
|
|
158
|
-
|
|
157
|
+
assert_equal @body, @response.body
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
test "should forward JSON error responses without double-escaping" do
|
|
161
|
+
json_error = { errors: [{ detail: 'inventory_id must exist', status: '404' }] }.to_json
|
|
162
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 404, "Not Found")
|
|
163
|
+
net_http_resp['content-type'] = 'application/json'
|
|
164
|
+
res = RestClient::Response.create(json_error, net_http_resp, @http_req)
|
|
165
|
+
::ForemanRhCloud::CloudRequestForwarder.any_instance.stubs(:execute_cloud_request).raises(RestClient::NotFound.new(res))
|
|
166
|
+
|
|
167
|
+
get :forward_request, params: { "path" => "api/vulnerability/v1/systems/00000000-0000-0000-0000-000000000000" }
|
|
168
|
+
assert_equal 404, @response.status
|
|
169
|
+
assert_includes @response.content_type, 'application/json'
|
|
170
|
+
assert_equal json_error, @response.body
|
|
171
|
+
parsed = JSON.parse(@response.body)
|
|
172
|
+
assert_equal 'inventory_id must exist', parsed['errors'][0]['detail']
|
|
159
173
|
end
|
|
160
174
|
|
|
161
175
|
test "should create insights facet" do
|
|
@@ -177,8 +177,22 @@ module InsightsCloud
|
|
|
177
177
|
|
|
178
178
|
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
|
179
179
|
assert_equal 500, @response.status
|
|
180
|
-
assert_equal
|
|
181
|
-
|
|
180
|
+
assert_equal @body, @response.body
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
test "should forward JSON error responses without double-escaping" do
|
|
184
|
+
json_error = { errors: [{ detail: 'inventory_id must exist', status: '404' }] }.to_json
|
|
185
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 404, "Not Found")
|
|
186
|
+
net_http_resp['content-type'] = 'application/json'
|
|
187
|
+
res = RestClient::Response.create(json_error, net_http_resp, @http_req)
|
|
188
|
+
::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:execute_cloud_request).raises(RestClient::NotFound.new(res))
|
|
189
|
+
|
|
190
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/systems/00000000-0000-0000-0000-000000000000" }, session: set_session
|
|
191
|
+
assert_equal 404, @response.status
|
|
192
|
+
assert_includes @response.content_type, 'application/json'
|
|
193
|
+
assert_equal json_error, @response.body
|
|
194
|
+
parsed = JSON.parse(@response.body)
|
|
195
|
+
assert_equal 'inventory_id must exist', parsed['errors'][0]['detail']
|
|
182
196
|
end
|
|
183
197
|
|
|
184
198
|
test "should allow forward_request with nil location (Any location)" do
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'test_plugin_helper'
|
|
2
|
+
require 'foreman_tasks/test_helpers'
|
|
3
|
+
|
|
4
|
+
class InsightsGenerateNotificationsTest < ActiveSupport::TestCase
|
|
5
|
+
include Dynflow::Testing::Factories
|
|
6
|
+
|
|
7
|
+
setup do
|
|
8
|
+
User.current = User.find_by(login: 'secret_admin')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
test 'skips notifications when foreman_host is nil' do
|
|
12
|
+
ForemanRhCloud.stubs(:foreman_host).returns(nil)
|
|
13
|
+
|
|
14
|
+
# Ensure blueprint exists or create it
|
|
15
|
+
NotificationBlueprint.find_or_create_by(name: 'insights_satellite_hits') do |bp|
|
|
16
|
+
bp.message = 'Test message'
|
|
17
|
+
bp.level = 'info'
|
|
18
|
+
bp.expires_in = 7.days
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
plan = ForemanTasks.sync_task(InsightsCloud::Async::InsightsGenerateNotifications)
|
|
22
|
+
|
|
23
|
+
# Should not raise an error, task completes successfully
|
|
24
|
+
assert_includes ['success', 'stopped'], plan.state, "Task should complete without error"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -106,4 +106,13 @@ class InventorySelfHostSyncTest < ActiveSupport::TestCase
|
|
|
106
106
|
|
|
107
107
|
assert_equal @host1_inventory_id, @host1.insights.uuid
|
|
108
108
|
end
|
|
109
|
+
|
|
110
|
+
test 'skips sync when foreman_host is nil' do
|
|
111
|
+
ForemanRhCloud.stubs(:foreman_host).returns(nil)
|
|
112
|
+
|
|
113
|
+
plan = ForemanTasks.sync_task(InventorySync::Async::InventorySelfHostSync)
|
|
114
|
+
|
|
115
|
+
# Task should be stopped (not executed) when host is nil, not failed
|
|
116
|
+
assert_equal 'stopped', plan.state
|
|
117
|
+
end
|
|
109
118
|
end
|
|
@@ -401,4 +401,33 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
|
|
|
401
401
|
file.close
|
|
402
402
|
end
|
|
403
403
|
end
|
|
404
|
+
|
|
405
|
+
test 'mark as done when upload aborted due to missing certificate' do
|
|
406
|
+
# Remove certificate from organization
|
|
407
|
+
Organization.any_instance.stubs(:owner_details).returns({})
|
|
408
|
+
|
|
409
|
+
action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
|
|
410
|
+
action.expects(:action_subject).with(@organization)
|
|
411
|
+
plan_action(action, nil, @organization.id)
|
|
412
|
+
|
|
413
|
+
# Execute the action
|
|
414
|
+
action.send(:try_execute)
|
|
415
|
+
|
|
416
|
+
# Verify it is marked as done
|
|
417
|
+
assert action.done?
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
test 'mark as done when upload aborted due to disconnected mode' do
|
|
421
|
+
Setting.stubs(:[]).with(:subscription_connection_enabled).returns(false)
|
|
422
|
+
|
|
423
|
+
action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
|
|
424
|
+
action.expects(:action_subject).with(@organization)
|
|
425
|
+
plan_action(action, nil, @organization.id)
|
|
426
|
+
|
|
427
|
+
# Execute the action
|
|
428
|
+
action.send(:try_execute)
|
|
429
|
+
|
|
430
|
+
# Verify it is marked as done
|
|
431
|
+
assert action.done?
|
|
432
|
+
end
|
|
404
433
|
end
|
|
@@ -2,8 +2,9 @@ require 'test_plugin_helper'
|
|
|
2
2
|
|
|
3
3
|
class ForemanRhCloudSelfHostTest < ActiveSupport::TestCase
|
|
4
4
|
setup do
|
|
5
|
-
# reset cached value
|
|
6
|
-
ForemanRhCloud.
|
|
5
|
+
# reset cached value - must remove the variable entirely, not just set to nil
|
|
6
|
+
ForemanRhCloud.remove_instance_variable(:@foreman_host) if ForemanRhCloud.instance_variable_defined?(:@foreman_host)
|
|
7
|
+
ENV.delete('SATELLITE_RH_CLOUD_FOREMAN_HOST')
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
test 'finds host by fullname' do
|
|
@@ -32,4 +33,51 @@ class ForemanRhCloudSelfHostTest < ActiveSupport::TestCase
|
|
|
32
33
|
|
|
33
34
|
assert_equal @host, actual
|
|
34
35
|
end
|
|
36
|
+
|
|
37
|
+
test 'returns nil when host does not exist' do
|
|
38
|
+
ForemanRhCloud.expects(:foreman_host_name).returns('nonexistent.example.com')
|
|
39
|
+
|
|
40
|
+
actual = ForemanRhCloud.foreman_host
|
|
41
|
+
|
|
42
|
+
assert_nil actual
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
test 'returns nil and does not query Host when foreman_host_name is nil' do
|
|
46
|
+
ForemanRhCloud.stubs(:foreman_host_name).returns(nil)
|
|
47
|
+
::Host.unscoped.friendly.expects(:where).never
|
|
48
|
+
|
|
49
|
+
assert_nil ForemanRhCloud.foreman_host
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
test 'caches nil value to avoid repeated lookups' do
|
|
53
|
+
ForemanRhCloud.expects(:foreman_host_name).once.returns('nonexistent.example.com')
|
|
54
|
+
|
|
55
|
+
2.times { ForemanRhCloud.foreman_host }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
test 'extracts hostname from foreman_url setting' do
|
|
59
|
+
Setting[:foreman_url] = 'https://satellite.example.com'
|
|
60
|
+
|
|
61
|
+
actual = ForemanRhCloud.foreman_url_hostname
|
|
62
|
+
|
|
63
|
+
assert_equal 'satellite.example.com', actual
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
test 'handles invalid foreman_url gracefully' do
|
|
67
|
+
# Stub Setting to return invalid URL without validation
|
|
68
|
+
Setting.stubs(:[]).with(:foreman_url).returns('not a valid url')
|
|
69
|
+
|
|
70
|
+
actual = ForemanRhCloud.foreman_url_hostname
|
|
71
|
+
|
|
72
|
+
assert_nil actual
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
test 'foreman_host_name uses foreman_url when marked_foreman_host is nil' do
|
|
76
|
+
ForemanRhCloud.expects(:marked_foreman_host).returns(nil)
|
|
77
|
+
ForemanRhCloud.expects(:foreman_url_hostname).returns('satellite.example.com')
|
|
78
|
+
|
|
79
|
+
actual = ForemanRhCloud.foreman_host_name
|
|
80
|
+
|
|
81
|
+
assert_equal 'satellite.example.com', actual
|
|
82
|
+
end
|
|
35
83
|
end
|
|
@@ -6,6 +6,10 @@ class InsightsFacetTest < ActiveSupport::TestCase
|
|
|
6
6
|
InsightsFacet.reset_counters(@host.insights.id, :hits_count)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
teardown do
|
|
10
|
+
ForemanRhCloud.unstub(:with_iop_smart_proxy?)
|
|
11
|
+
end
|
|
12
|
+
|
|
9
13
|
test 'host with hits can be deleted' do
|
|
10
14
|
assert_equal 1, @host.insights.hits.count
|
|
11
15
|
|
|
@@ -16,6 +20,7 @@ class InsightsFacetTest < ActiveSupport::TestCase
|
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
test 'search host by recommendations_count' do
|
|
23
|
+
ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
|
|
19
24
|
FactoryBot.create(:host) # create another host with no recommendations
|
|
20
25
|
|
|
21
26
|
assert_equal 1, Host.search_for('insights_recommendations_count = 1').count
|
|
@@ -2,7 +2,8 @@ require 'test_plugin_helper'
|
|
|
2
2
|
|
|
3
3
|
class MetadataGeneratorTest < ActiveSupport::TestCase
|
|
4
4
|
setup do
|
|
5
|
-
|
|
5
|
+
# reset cached value - must remove the variable entirely, not just set to nil
|
|
6
|
+
ForemanRhCloud.remove_instance_variable(:@foreman_host) if ForemanRhCloud.instance_variable_defined?(:@foreman_host)
|
|
6
7
|
end
|
|
7
8
|
|
|
8
9
|
test 'generates an empty report' do
|
|
@@ -64,4 +65,26 @@ class MetadataGeneratorTest < ActiveSupport::TestCase
|
|
|
64
65
|
assert_not_nil(slice = slices['test_12345'])
|
|
65
66
|
assert_equal 3, slice['number_hosts']
|
|
66
67
|
end
|
|
68
|
+
|
|
69
|
+
test 'generates metadata when foreman_host is nil' do
|
|
70
|
+
ForemanRhCloud.stubs(:foreman_host).returns(nil)
|
|
71
|
+
ForemanRhCloud.stubs(:foreman_host_name).returns('satellite.example.com')
|
|
72
|
+
|
|
73
|
+
generator = ForemanInventoryUpload::Generators::Metadata.new
|
|
74
|
+
|
|
75
|
+
# Should not raise an error
|
|
76
|
+
json_str = nil
|
|
77
|
+
assert_nothing_raised do
|
|
78
|
+
json_str = generator.render do
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Verify hostname is from foreman_host_name
|
|
83
|
+
actual = JSON.parse(json_str.join("\n"))
|
|
84
|
+
assert_equal 'satellite.example.com', actual['reporting_host_name']
|
|
85
|
+
# Verify IP and BIOS UUID fields are nil when host is nil
|
|
86
|
+
# This is acceptable per SAT-25889 - cloud services don't rely on these fields
|
|
87
|
+
assert_nil actual['reporting_host_ips']
|
|
88
|
+
assert_nil actual['reporting_host_bios_uuid']
|
|
89
|
+
end
|
|
67
90
|
end
|