foreman_rh_cloud 13.2.8 → 13.2.10
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/insights_cloud/candlepin_proxies_extensions.rb +23 -0
- data/app/controllers/concerns/insights_cloud/package_profile_upload_extensions.rb +9 -0
- data/app/controllers/insights_cloud/api/machine_telemetries_controller.rb +13 -10
- data/app/controllers/insights_cloud/ui_requests_controller.rb +3 -7
- data/app/models/concerns/rh_cloud_host.rb +4 -2
- data/app/models/insights_client_report_status.rb +9 -1
- data/app/models/inventory_sync/inventory_status.rb +16 -4
- data/lib/foreman_inventory_upload/generators/fact_helpers.rb +26 -4
- data/lib/foreman_inventory_upload.rb +8 -1
- data/lib/foreman_rh_cloud/engine.rb +1 -0
- 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_full_sync.rb +39 -3
- 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 +56 -2
- data/test/controllers/insights_cloud/candlepin_proxies_extensions_test.rb +70 -0
- data/test/controllers/insights_cloud/ui_requests_controller_test.rb +16 -2
- data/test/jobs/insights_client_status_aging_test.rb +40 -0
- data/test/jobs/insights_generate_notifications_test.rb +26 -0
- data/test/jobs/inventory_full_sync_test.rb +212 -0
- data/test/jobs/inventory_self_host_sync_test.rb +9 -0
- data/test/models/insights_client_report_status_test.rb +109 -0
- data/test/models/inventory_sync/inventory_status_test.rb +85 -0
- data/test/unit/foreman_rh_cloud_self_host_test.rb +50 -2
- data/test/unit/metadata_generator_test.rb +24 -1
- data/test/unit/rh_cloud_host_test.rb +60 -0
- data/webpack/CVEsHostDetailsTab/CVEsHostDetailsTab.js +24 -2
- data/webpack/CVEsHostDetailsTab/__tests__/CVEsHostDetailsTab.test.js +73 -10
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/SyncButtonActions.js +8 -2
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/integrations.test.js.snap +1 -0
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/integrations.test.js +1 -0
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/components/Toast.js +43 -17
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/components/__tests__/Toast.test.js +82 -0
- data/webpack/ForemanRhCloudHelpers.js +22 -0
- data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +27 -1
- data/webpack/InsightsHostDetailsTab/__tests__/NewHostDetailsTab.test.js +134 -22
- metadata +16 -2
|
@@ -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
|
|
@@ -31,4 +31,44 @@ class InsightsClientStatusAgingTest < ActiveSupport::TestCase
|
|
|
31
31
|
assert_equal InsightsClientReportStatus::NO_REPORT, @host3.get_status(InsightsClientReportStatus).status
|
|
32
32
|
assert_equal InsightsClientReportStatus::NO_REPORT, @host4.get_status(InsightsClientReportStatus).status
|
|
33
33
|
end
|
|
34
|
+
|
|
35
|
+
test 'aging job does not affect USER_OMITTED hosts' do
|
|
36
|
+
# Host 1: USER_OMITTED with old reported_at (should stay USER_OMITTED)
|
|
37
|
+
InsightsClientReportStatus.find_or_initialize_by(host_id: @host1.id).update(
|
|
38
|
+
status: InsightsClientReportStatus::USER_OMITTED,
|
|
39
|
+
reported_at: Time.now - InsightsClientReportStatus::REPORT_INTERVAL - 1.day
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Host 2: REPORTING with old reported_at (should change to NO_REPORT)
|
|
43
|
+
InsightsClientReportStatus.find_or_initialize_by(host_id: @host2.id).update(
|
|
44
|
+
status: InsightsClientReportStatus::REPORTING,
|
|
45
|
+
reported_at: Time.now - InsightsClientReportStatus::REPORT_INTERVAL - 1.day
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Host 3: USER_OMITTED with recent reported_at (should stay USER_OMITTED)
|
|
49
|
+
InsightsClientReportStatus.find_or_initialize_by(host_id: @host3.id).update(
|
|
50
|
+
status: InsightsClientReportStatus::USER_OMITTED,
|
|
51
|
+
reported_at: Time.now - 1.day
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Host 4: REPORTING with recent reported_at (should stay REPORTING)
|
|
55
|
+
InsightsClientReportStatus.find_or_initialize_by(host_id: @host4.id).update(
|
|
56
|
+
status: InsightsClientReportStatus::REPORTING,
|
|
57
|
+
reported_at: Time.now - 1.day
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
action = create_and_plan_action(InsightsCloud::Async::InsightsClientStatusAging)
|
|
61
|
+
run_action(action)
|
|
62
|
+
|
|
63
|
+
@hosts.each(&:reload)
|
|
64
|
+
|
|
65
|
+
assert_equal InsightsClientReportStatus::USER_OMITTED, @host1.get_status(InsightsClientReportStatus).status,
|
|
66
|
+
'USER_OMITTED host with old report should stay USER_OMITTED'
|
|
67
|
+
assert_equal InsightsClientReportStatus::NO_REPORT, @host2.get_status(InsightsClientReportStatus).status,
|
|
68
|
+
'REPORTING host with old report should change to NO_REPORT'
|
|
69
|
+
assert_equal InsightsClientReportStatus::USER_OMITTED, @host3.get_status(InsightsClientReportStatus).status,
|
|
70
|
+
'USER_OMITTED host with recent report should stay USER_OMITTED'
|
|
71
|
+
assert_equal InsightsClientReportStatus::REPORTING, @host4.get_status(InsightsClientReportStatus).status,
|
|
72
|
+
'REPORTING host with recent report should stay REPORTING'
|
|
73
|
+
end
|
|
34
74
|
end
|
|
@@ -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
|
|
@@ -5,6 +5,7 @@ class InventoryFullSyncTest < ActiveSupport::TestCase
|
|
|
5
5
|
include Dynflow::Testing::Factories
|
|
6
6
|
include MockCerts
|
|
7
7
|
include KatelloCVEHelper
|
|
8
|
+
include CandlepinIsolation
|
|
8
9
|
|
|
9
10
|
setup do
|
|
10
11
|
User.current = User.find_by(login: 'secret_admin')
|
|
@@ -313,4 +314,215 @@ class InventoryFullSyncTest < ActiveSupport::TestCase
|
|
|
313
314
|
|
|
314
315
|
assert_nil InventorySync::InventoryStatus.where(host_id: @host3.id).first
|
|
315
316
|
end
|
|
317
|
+
|
|
318
|
+
test 'user-omitted hosts get USER_OMITTED status' do
|
|
319
|
+
# Add parameter to exclude host1
|
|
320
|
+
@host1.host_parameters << HostParameter.create(
|
|
321
|
+
name: 'host_registration_insights_inventory',
|
|
322
|
+
value: 'false',
|
|
323
|
+
parameter_type: 'boolean'
|
|
324
|
+
)
|
|
325
|
+
@host1.save!
|
|
326
|
+
|
|
327
|
+
setup_certs_expectation do
|
|
328
|
+
InventorySync::Async::InventoryFullSync.any_instance.stubs(:candlepin_id_cert)
|
|
329
|
+
end
|
|
330
|
+
InventorySync::Async::InventoryFullSync.any_instance.expects(:query_inventory).returns(@inventory)
|
|
331
|
+
FactoryBot.create(:fact_value, fact_name: fact_names['virt::uuid'], value: '1234', host: @host2)
|
|
332
|
+
|
|
333
|
+
action = create_and_plan_action(InventorySync::Async::InventoryFullSync, @host1.organization)
|
|
334
|
+
run_action(action)
|
|
335
|
+
|
|
336
|
+
@host1.reload
|
|
337
|
+
@host2.reload
|
|
338
|
+
|
|
339
|
+
# Host1 should be USER_OMITTED
|
|
340
|
+
assert_equal InventorySync::InventoryStatus::USER_OMITTED,
|
|
341
|
+
InventorySync::InventoryStatus.where(host_id: @host1.id).first.status,
|
|
342
|
+
'Host with host_registration_insights_inventory=false should have USER_OMITTED status'
|
|
343
|
+
|
|
344
|
+
# Host2 should be SYNC
|
|
345
|
+
assert_equal InventorySync::InventoryStatus::SYNC,
|
|
346
|
+
InventorySync::InventoryStatus.where(host_id: @host2.id).first.status,
|
|
347
|
+
'Normal host should have SYNC status'
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
test 'user-omitted hosts are not marked as disconnected' do
|
|
351
|
+
# Add parameter to exclude host1
|
|
352
|
+
@host1.host_parameters << HostParameter.create(
|
|
353
|
+
name: 'host_registration_insights_inventory',
|
|
354
|
+
value: 'false',
|
|
355
|
+
parameter_type: 'boolean'
|
|
356
|
+
)
|
|
357
|
+
@host1.save!
|
|
358
|
+
|
|
359
|
+
# Host1 is not in the cloud inventory response
|
|
360
|
+
setup_certs_expectation do
|
|
361
|
+
InventorySync::Async::InventoryFullSync.any_instance.stubs(:candlepin_id_cert)
|
|
362
|
+
end
|
|
363
|
+
InventorySync::Async::InventoryFullSync.any_instance.expects(:query_inventory).returns(@inventory)
|
|
364
|
+
InventorySync::Async::InventoryFullSync.any_instance.expects(:affected_host_ids).returns([@host1.id, @host2.id])
|
|
365
|
+
FactoryBot.create(:fact_value, fact_name: fact_names['virt::uuid'], value: '1234', host: @host2)
|
|
366
|
+
|
|
367
|
+
action = create_and_plan_action(InventorySync::Async::InventoryFullSync, @host1.organization)
|
|
368
|
+
run_action(action)
|
|
369
|
+
|
|
370
|
+
@host1.reload
|
|
371
|
+
|
|
372
|
+
# Host1 should be USER_OMITTED, not DISCONNECT
|
|
373
|
+
assert_equal InventorySync::InventoryStatus::USER_OMITTED,
|
|
374
|
+
InventorySync::InventoryStatus.where(host_id: @host1.id).first.status,
|
|
375
|
+
'User-omitted host should have USER_OMITTED status even if not in cloud inventory'
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
test 'host_statuses output includes all three counts' do
|
|
379
|
+
# Create a mix of hosts with different statuses
|
|
380
|
+
# Host1: will be user-omitted
|
|
381
|
+
@host1.host_parameters << HostParameter.create(
|
|
382
|
+
name: 'host_registration_insights_inventory',
|
|
383
|
+
value: 'false',
|
|
384
|
+
parameter_type: 'boolean'
|
|
385
|
+
)
|
|
386
|
+
@host1.save!
|
|
387
|
+
|
|
388
|
+
# Host2: will be synced (in cloud inventory)
|
|
389
|
+
# Host3: will be disconnected (not in cloud inventory)
|
|
390
|
+
|
|
391
|
+
# Create inventory response with only host2 (exclude host1 and host3)
|
|
392
|
+
inventory_with_host2_only = @inventory.dup
|
|
393
|
+
inventory_with_host2_only['results'] = [@inventory['results'][0]] # Only host2
|
|
394
|
+
inventory_with_host2_only['total'] = 1
|
|
395
|
+
inventory_with_host2_only['count'] = 1
|
|
396
|
+
|
|
397
|
+
setup_certs_expectation do
|
|
398
|
+
InventorySync::Async::InventoryFullSync.any_instance.stubs(:candlepin_id_cert)
|
|
399
|
+
end
|
|
400
|
+
InventorySync::Async::InventoryFullSync.any_instance.expects(:query_inventory).returns(inventory_with_host2_only)
|
|
401
|
+
InventorySync::Async::InventoryFullSync.any_instance.expects(:affected_host_ids).returns([@host1.id, @host2.id, @host3.id])
|
|
402
|
+
FactoryBot.create(:fact_value, fact_name: fact_names['virt::uuid'], value: '1234', host: @host2)
|
|
403
|
+
|
|
404
|
+
action = create_and_plan_action(InventorySync::Async::InventoryFullSync, @host1.organization)
|
|
405
|
+
run_action(action)
|
|
406
|
+
|
|
407
|
+
# Verify the statuses were actually set correctly
|
|
408
|
+
@host1.reload
|
|
409
|
+
@host2.reload
|
|
410
|
+
@host3.reload
|
|
411
|
+
|
|
412
|
+
assert_equal InventorySync::InventoryStatus::USER_OMITTED,
|
|
413
|
+
InventorySync::InventoryStatus.where(host_id: @host1.id).first.status,
|
|
414
|
+
'Host with host_registration_insights_inventory=false should have USER_OMITTED status'
|
|
415
|
+
assert_equal InventorySync::InventoryStatus::SYNC,
|
|
416
|
+
InventorySync::InventoryStatus.where(host_id: @host2.id).first.status,
|
|
417
|
+
'Host in cloud inventory should have SYNC status'
|
|
418
|
+
assert_equal InventorySync::InventoryStatus::DISCONNECT,
|
|
419
|
+
InventorySync::InventoryStatus.where(host_id: @host3.id).first.status,
|
|
420
|
+
'Host not in cloud inventory and not user-omitted should have DISCONNECT status'
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
test 'user-omitted status respects parameter inheritance from hostgroup' do
|
|
424
|
+
# Create a hostgroup and assign it to host1
|
|
425
|
+
hostgroup = FactoryBot.create(:hostgroup)
|
|
426
|
+
hostgroup.organizations << @host1.organization
|
|
427
|
+
@host1.hostgroup = hostgroup
|
|
428
|
+
@host1.save!
|
|
429
|
+
|
|
430
|
+
# Set parameter at hostgroup level, not directly on host
|
|
431
|
+
# This verifies the fix that uses search_for instead of querying HostParameter directly
|
|
432
|
+
hostgroup.group_parameters << GroupParameter.create(
|
|
433
|
+
name: 'host_registration_insights_inventory',
|
|
434
|
+
value: 'false',
|
|
435
|
+
key_type: 'boolean'
|
|
436
|
+
)
|
|
437
|
+
hostgroup.save!
|
|
438
|
+
|
|
439
|
+
# Verify parameter is inherited (not set directly on host)
|
|
440
|
+
assert_nil @host1.parameters.find_by(name: 'host_registration_insights_inventory'),
|
|
441
|
+
'Test setup: parameter should not be set directly on host'
|
|
442
|
+
refute ::Foreman::Cast.to_bool(@host1.host_param('host_registration_insights_inventory')),
|
|
443
|
+
'Test setup: parameter should be inherited from hostgroup'
|
|
444
|
+
|
|
445
|
+
# Host2 remains normal (no parameter)
|
|
446
|
+
setup_certs_expectation do
|
|
447
|
+
InventorySync::Async::InventoryFullSync.any_instance.stubs(:candlepin_id_cert)
|
|
448
|
+
end
|
|
449
|
+
InventorySync::Async::InventoryFullSync.any_instance.expects(:query_inventory).returns(@inventory)
|
|
450
|
+
InventorySync::Async::InventoryFullSync.any_instance.expects(:affected_host_ids).returns([@host1.id, @host2.id])
|
|
451
|
+
FactoryBot.create(:fact_value, fact_name: fact_names['virt::uuid'], value: '1234', host: @host2)
|
|
452
|
+
|
|
453
|
+
action = create_and_plan_action(InventorySync::Async::InventoryFullSync, @host1.organization)
|
|
454
|
+
run_action(action)
|
|
455
|
+
|
|
456
|
+
@host1.reload
|
|
457
|
+
@host2.reload
|
|
458
|
+
|
|
459
|
+
# Host1 should be USER_OMITTED (inherited parameter from hostgroup)
|
|
460
|
+
host1_status = InventorySync::InventoryStatus.where(host_id: @host1.id).first
|
|
461
|
+
assert_not_nil host1_status, 'Host1 should have an inventory status'
|
|
462
|
+
assert_equal InventorySync::InventoryStatus::USER_OMITTED, host1_status.status,
|
|
463
|
+
'Host with inherited host_registration_insights_inventory=false should have USER_OMITTED status'
|
|
464
|
+
|
|
465
|
+
# Host2 should be SYNC
|
|
466
|
+
assert_equal InventorySync::InventoryStatus::SYNC,
|
|
467
|
+
InventorySync::InventoryStatus.where(host_id: @host2.id).first.status,
|
|
468
|
+
'Normal host should have SYNC status'
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
test 'user-omitted statuses are cleared before re-creating them to avoid silent create failures' do
|
|
472
|
+
# First sync: host1 is user-omitted
|
|
473
|
+
@host1.host_parameters << HostParameter.create(
|
|
474
|
+
name: 'host_registration_insights_inventory',
|
|
475
|
+
value: 'false',
|
|
476
|
+
parameter_type: 'boolean'
|
|
477
|
+
)
|
|
478
|
+
@host1.save!
|
|
479
|
+
|
|
480
|
+
setup_certs_expectation do
|
|
481
|
+
InventorySync::Async::InventoryFullSync.any_instance.stubs(:candlepin_id_cert)
|
|
482
|
+
end
|
|
483
|
+
InventorySync::Async::InventoryFullSync.any_instance.expects(:query_inventory).returns(@inventory).twice
|
|
484
|
+
InventorySync::Async::InventoryFullSync.any_instance.expects(:affected_host_ids).returns([@host1.id, @host2.id]).twice
|
|
485
|
+
FactoryBot.create(:fact_value, fact_name: fact_names['virt::uuid'], value: '1234', host: @host2)
|
|
486
|
+
|
|
487
|
+
# Run first sync
|
|
488
|
+
action = create_and_plan_action(InventorySync::Async::InventoryFullSync, @host1.organization)
|
|
489
|
+
run_action(action)
|
|
490
|
+
|
|
491
|
+
@host1.reload
|
|
492
|
+
initial_status = InventorySync::InventoryStatus.where(host_id: @host1.id).first
|
|
493
|
+
assert_equal InventorySync::InventoryStatus::USER_OMITTED, initial_status.status,
|
|
494
|
+
'Initial sync: host should be USER_OMITTED'
|
|
495
|
+
initial_status_id = initial_status.id
|
|
496
|
+
|
|
497
|
+
# Verify there's only one status record for host1
|
|
498
|
+
assert_equal 1, InventorySync::InventoryStatus.where(host_id: @host1.id).count,
|
|
499
|
+
'Should have exactly one status record after first sync'
|
|
500
|
+
|
|
501
|
+
# Run second sync (parameter still false)
|
|
502
|
+
# Without clearing old user_omitted statuses, the .create would silently fail
|
|
503
|
+
# with 'Host has already been taken' due to uniqueness constraint
|
|
504
|
+
setup_certs_expectation do
|
|
505
|
+
InventorySync::Async::InventoryFullSync.any_instance.stubs(:candlepin_id_cert)
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
action2 = create_and_plan_action(InventorySync::Async::InventoryFullSync, @host1.organization)
|
|
509
|
+
run_action(action2)
|
|
510
|
+
|
|
511
|
+
@host1.reload
|
|
512
|
+
final_status = InventorySync::InventoryStatus.where(host_id: @host1.id).first
|
|
513
|
+
|
|
514
|
+
# Verify status is still USER_OMITTED (not stale from first sync)
|
|
515
|
+
assert_equal InventorySync::InventoryStatus::USER_OMITTED, final_status.status,
|
|
516
|
+
'Status should be USER_OMITTED after second sync'
|
|
517
|
+
|
|
518
|
+
# Verify there's still only one status record (old one was deleted before creating new one)
|
|
519
|
+
assert_equal 1, InventorySync::InventoryStatus.where(host_id: @host1.id).count,
|
|
520
|
+
'Should have exactly one status record (old cleared before new created)'
|
|
521
|
+
|
|
522
|
+
# Verify the old status record was actually deleted and replaced with a new one
|
|
523
|
+
refute InventorySync::InventoryStatus.exists?(initial_status_id),
|
|
524
|
+
'Old status record should have been deleted before creating new one'
|
|
525
|
+
assert_not_equal initial_status_id, final_status.id,
|
|
526
|
+
'New status record should have different ID, proving old was deleted'
|
|
527
|
+
end
|
|
316
528
|
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
|
|
@@ -72,4 +72,113 @@ class InsightsClientReportStatusTest < ActiveSupport::TestCase
|
|
|
72
72
|
|
|
73
73
|
assert_equal HostStatus::Global::ERROR, @host.global_status
|
|
74
74
|
end
|
|
75
|
+
|
|
76
|
+
test 'host with host_registration_insights parameter set to false gets USER_OMITTED status' do
|
|
77
|
+
@host.host_parameters << HostParameter.create(
|
|
78
|
+
name: 'host_registration_insights',
|
|
79
|
+
value: 'false',
|
|
80
|
+
parameter_type: 'boolean'
|
|
81
|
+
)
|
|
82
|
+
@host.save!
|
|
83
|
+
|
|
84
|
+
insights_status = @host.get_status(InsightsClientReportStatus)
|
|
85
|
+
insights_status.refresh!
|
|
86
|
+
|
|
87
|
+
assert_equal InsightsClientReportStatus::USER_OMITTED, insights_status.status
|
|
88
|
+
assert_equal HostStatus::Global::OK, insights_status.to_global
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
test 'USER_OMITTED status has correct label' do
|
|
92
|
+
insights_status = @host.get_status(InsightsClientReportStatus)
|
|
93
|
+
insights_status.status = InsightsClientReportStatus::USER_OMITTED
|
|
94
|
+
insights_status.save!
|
|
95
|
+
|
|
96
|
+
label = insights_status.to_label
|
|
97
|
+
assert_match(/host_registration_insights/, label)
|
|
98
|
+
assert_match(/false/, label)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
test 'stale scope excludes USER_OMITTED hosts' do
|
|
102
|
+
host1 = FactoryBot.create(:host, :managed)
|
|
103
|
+
host2 = FactoryBot.create(:host, :managed)
|
|
104
|
+
host3 = FactoryBot.create(:host, :managed)
|
|
105
|
+
|
|
106
|
+
# Host 1: USER_OMITTED with old reported_at (should NOT be in stale scope)
|
|
107
|
+
status1 = host1.get_status(InsightsClientReportStatus)
|
|
108
|
+
status1.status = InsightsClientReportStatus::USER_OMITTED
|
|
109
|
+
status1.reported_at = Time.zone.now - InsightsClientReportStatus::REPORT_INTERVAL - 1.day
|
|
110
|
+
status1.save!
|
|
111
|
+
|
|
112
|
+
# Host 2: REPORTING with old reported_at (should be in stale scope)
|
|
113
|
+
status2 = host2.get_status(InsightsClientReportStatus)
|
|
114
|
+
status2.status = InsightsClientReportStatus::REPORTING
|
|
115
|
+
status2.reported_at = Time.zone.now - InsightsClientReportStatus::REPORT_INTERVAL - 1.day
|
|
116
|
+
status2.save!
|
|
117
|
+
|
|
118
|
+
# Host 3: NO_REPORT with old reported_at (should be in stale scope)
|
|
119
|
+
status3 = host3.get_status(InsightsClientReportStatus)
|
|
120
|
+
status3.status = InsightsClientReportStatus::NO_REPORT
|
|
121
|
+
status3.reported_at = Time.zone.now - InsightsClientReportStatus::REPORT_INTERVAL - 1.day
|
|
122
|
+
status3.save!
|
|
123
|
+
|
|
124
|
+
stale_statuses = InsightsClientReportStatus.stale
|
|
125
|
+
stale_host_ids = stale_statuses.pluck(:host_id)
|
|
126
|
+
|
|
127
|
+
assert_not_includes stale_host_ids, host1.id, 'USER_OMITTED host should not be in stale scope'
|
|
128
|
+
assert_includes stale_host_ids, host2.id, 'REPORTING host with old report should be in stale scope'
|
|
129
|
+
assert_includes stale_host_ids, host3.id, 'NO_REPORT host with old report should be in stale scope'
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
test 'USER_OMITTED status respects parameter inheritance from hostgroup' do
|
|
133
|
+
# Create a hostgroup with parameter = false
|
|
134
|
+
hostgroup = FactoryBot.create(:hostgroup)
|
|
135
|
+
hostgroup.group_parameters << GroupParameter.create(
|
|
136
|
+
name: 'host_registration_insights',
|
|
137
|
+
value: 'false',
|
|
138
|
+
key_type: 'boolean'
|
|
139
|
+
)
|
|
140
|
+
hostgroup.save!
|
|
141
|
+
|
|
142
|
+
@host.hostgroup = hostgroup
|
|
143
|
+
@host.save!
|
|
144
|
+
|
|
145
|
+
# Verify parameter is inherited (not set directly on host)
|
|
146
|
+
assert_nil @host.parameters.find_by(name: 'host_registration_insights'),
|
|
147
|
+
'Test setup: parameter should not be set directly on host'
|
|
148
|
+
|
|
149
|
+
insights_status = @host.get_status(InsightsClientReportStatus)
|
|
150
|
+
insights_status.refresh!
|
|
151
|
+
|
|
152
|
+
assert_equal InsightsClientReportStatus::USER_OMITTED, insights_status.status,
|
|
153
|
+
'Status should be USER_OMITTED when host_registration_insights=false is inherited from hostgroup'
|
|
154
|
+
assert_equal HostStatus::Global::OK, insights_status.to_global,
|
|
155
|
+
'USER_OMITTED status should not affect global status'
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
test 'host parameter overrides inherited parameter from hostgroup' do
|
|
159
|
+
# Create a hostgroup with parameter = false
|
|
160
|
+
hostgroup = FactoryBot.create(:hostgroup)
|
|
161
|
+
hostgroup.group_parameters << GroupParameter.create(
|
|
162
|
+
name: 'host_registration_insights',
|
|
163
|
+
value: 'false',
|
|
164
|
+
key_type: 'boolean'
|
|
165
|
+
)
|
|
166
|
+
hostgroup.save!
|
|
167
|
+
|
|
168
|
+
@host.hostgroup = hostgroup
|
|
169
|
+
|
|
170
|
+
# Override with host parameter = true
|
|
171
|
+
@host.host_parameters << HostParameter.create(
|
|
172
|
+
name: 'host_registration_insights',
|
|
173
|
+
value: 'true',
|
|
174
|
+
parameter_type: 'boolean'
|
|
175
|
+
)
|
|
176
|
+
@host.save!
|
|
177
|
+
|
|
178
|
+
insights_status = @host.get_status(InsightsClientReportStatus)
|
|
179
|
+
insights_status.refresh!
|
|
180
|
+
|
|
181
|
+
assert_not_equal InsightsClientReportStatus::USER_OMITTED, insights_status.status,
|
|
182
|
+
'Status should not be USER_OMITTED when host parameter overrides hostgroup parameter with true'
|
|
183
|
+
end
|
|
75
184
|
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require 'test_plugin_helper'
|
|
2
|
+
|
|
3
|
+
module InventorySync
|
|
4
|
+
class InventoryStatusTest < ActiveSupport::TestCase
|
|
5
|
+
setup do
|
|
6
|
+
@host = FactoryBot.create(:host, :managed)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
test 'status constants are defined correctly' do
|
|
10
|
+
assert_equal 0, InventorySync::InventoryStatus::DISCONNECT
|
|
11
|
+
assert_equal 1, InventorySync::InventoryStatus::SYNC
|
|
12
|
+
assert_equal 2, InventorySync::InventoryStatus::USER_OMITTED
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
test 'to_global returns OK for USER_OMITTED status' do
|
|
16
|
+
status = @host.get_status(InventorySync::InventoryStatus)
|
|
17
|
+
status.status = InventorySync::InventoryStatus::USER_OMITTED
|
|
18
|
+
status.save!
|
|
19
|
+
|
|
20
|
+
assert_equal HostStatus::Global::OK, status.to_global
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
test 'to_global returns WARN for DISCONNECT status' do
|
|
24
|
+
status = @host.get_status(InventorySync::InventoryStatus)
|
|
25
|
+
status.status = InventorySync::InventoryStatus::DISCONNECT
|
|
26
|
+
status.save!
|
|
27
|
+
|
|
28
|
+
assert_equal HostStatus::Global::WARN, status.to_global
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
test 'to_global returns OK for SYNC status' do
|
|
32
|
+
status = @host.get_status(InventorySync::InventoryStatus)
|
|
33
|
+
status.status = InventorySync::InventoryStatus::SYNC
|
|
34
|
+
status.save!
|
|
35
|
+
|
|
36
|
+
assert_equal HostStatus::Global::OK, status.to_global
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
test 'to_label returns appropriate messages for each status' do
|
|
40
|
+
status = @host.get_status(InventorySync::InventoryStatus)
|
|
41
|
+
|
|
42
|
+
# Test DISCONNECT label
|
|
43
|
+
status.status = InventorySync::InventoryStatus::DISCONNECT
|
|
44
|
+
status.save!
|
|
45
|
+
label = status.to_label
|
|
46
|
+
assert_match(/not present/, label.downcase)
|
|
47
|
+
assert_match(/console\.redhat\.com/, label)
|
|
48
|
+
|
|
49
|
+
# Test SYNC label
|
|
50
|
+
status.status = InventorySync::InventoryStatus::SYNC
|
|
51
|
+
status.save!
|
|
52
|
+
label = status.to_label
|
|
53
|
+
assert_match(/uploaded.*present/, label.downcase)
|
|
54
|
+
assert_match(/console\.redhat\.com/, label)
|
|
55
|
+
|
|
56
|
+
# Test USER_OMITTED label
|
|
57
|
+
status.status = InventorySync::InventoryStatus::USER_OMITTED
|
|
58
|
+
status.save!
|
|
59
|
+
label = status.to_label
|
|
60
|
+
assert_match(/excluded/, label.downcase)
|
|
61
|
+
assert_match(/host parameter/, label.downcase)
|
|
62
|
+
assert_match(/console\.redhat\.com/, label)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
test 'relevant? returns true in regular (non-IoP) mode' do
|
|
66
|
+
ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
|
|
67
|
+
|
|
68
|
+
status = @host.get_status(InventorySync::InventoryStatus)
|
|
69
|
+
status.status = InventorySync::InventoryStatus::SYNC
|
|
70
|
+
status.save!
|
|
71
|
+
|
|
72
|
+
assert status.relevant?, 'Inventory status should be relevant in regular mode'
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
test 'relevant? returns false in IoP mode' do
|
|
76
|
+
ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
|
|
77
|
+
|
|
78
|
+
status = @host.get_status(InventorySync::InventoryStatus)
|
|
79
|
+
status.status = InventorySync::InventoryStatus::SYNC
|
|
80
|
+
status.save!
|
|
81
|
+
|
|
82
|
+
refute status.relevant?, 'Inventory status should NOT be relevant in IoP mode'
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
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
|
|
@@ -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
|