diff --git a/api/host.py b/api/host.py index ba439ba85..07ff2c17e 100644 --- a/api/host.py +++ b/api/host.py @@ -243,6 +243,9 @@ def delete_hosts_by_filter( def _delete_host_list(host_id_list, rbac_filter): + frontend_origin = flask.request.headers.get("x-rh-frontend-origin", "") + initiated_by_frontend = frontend_origin == "hcc" + current_identity = get_current_identity() payload_tracker = get_payload_tracker( account=current_identity.account_number, org_id=current_identity.org_id, request_id=threadctx.request_id @@ -260,6 +263,7 @@ def _delete_host_list(host_id_list, rbac_filter): inventory_config().host_delete_chunk_size, identity=current_identity, control_rule=get_control_rule(), + initiated_by_frontend=initiated_by_frontend, ) deleted_id_list = [str(r.host_row.id) for r in result_list] diff --git a/app/queue/events.py b/app/queue/events.py index 5421e5799..59e8d35ae 100644 --- a/app/queue/events.py +++ b/app/queue/events.py @@ -35,7 +35,7 @@ class SerializedHostSchema(Schema): account = fields.Str(required=False) org_id = fields.Str(required=True) insights_id = fields.Str() - subscription_manager_id = fields.Str() + subscription_manager_id = fields.UUID() satellite_id = fields.Str() fqdn = fields.Str() bios_uuid = fields.Str() @@ -76,6 +76,8 @@ class HostDeleteEvent(Schema): org_id = fields.Str() insights_id = fields.Str() request_id = fields.Str() + subscription_manager_id = fields.UUID() + initiated_by_frontend = fields.Bool() platform_metadata = fields.Dict() metadata = fields.Nested(HostEventMetadataSchema()) @@ -113,7 +115,7 @@ def host_create_update_event(event_type, host, platform_metadata=None): ) -def host_delete_event(event_type, host, platform_metadata=None): +def host_delete_event(event_type, host, initiated_by_frontend=False, platform_metadata=None): delete_event = { "timestamp": datetime.now(timezone.utc), "type": event_type.name, @@ -121,6 +123,7 @@ def host_delete_event(event_type, host, platform_metadata=None): **serialize_canonical_facts(host.canonical_facts), "org_id": host.org_id, "account": host.account, + "initiated_by_frontend": initiated_by_frontend, "request_id": threadctx.request_id, "platform_metadata": platform_metadata, "metadata": {"request_id": threadctx.request_id}, diff --git a/app/queue/host_mq.py b/app/queue/host_mq.py index d7d7f6bab..58ed7d755 100644 --- a/app/queue/host_mq.py +++ b/app/queue/host_mq.py @@ -335,8 +335,13 @@ def handle_message(message, notification_event_producer, message_operation=add_h raise -def write_delete_event_message(event_producer: EventProducer, result: OperationResult): - event = build_event(EventType.delete, result.host_row, platform_metadata=result.platform_metadata) +def write_delete_event_message(event_producer: EventProducer, result: OperationResult, initiated_by_frontend: bool): + event = build_event( + EventType.delete, + result.host_row, + platform_metadata=result.platform_metadata, + initiated_by_frontend=initiated_by_frontend, + ) headers = message_headers( EventType.delete, result.host_row.canonical_facts.get("insights_id"), diff --git a/lib/host_delete.py b/lib/host_delete.py index 7387fb813..eb8a173b8 100644 --- a/lib/host_delete.py +++ b/lib/host_delete.py @@ -40,12 +40,15 @@ def _delete_host_db_records(select_query, chunk_size, identity, interrupt, contr def _send_delete_messages_for_batch( - processed_rows: list[OperationResult], event_producer: EventProducer, notification_event_producer: EventProducer + processed_rows: list[OperationResult], + event_producer: EventProducer, + notification_event_producer: EventProducer, + initiated_by_frontend: bool, ): for result in processed_rows: if result is not None: delete_host_count.inc() - write_delete_event_message(event_producer, result) + write_delete_event_message(event_producer, result, initiated_by_frontend) send_notification(notification_event_producer, NotificationType.system_deleted, vars(result.host_row)) @@ -57,12 +60,15 @@ def delete_hosts( interrupt=lambda: False, identity=None, control_rule=None, + initiated_by_frontend=False, ): while select_query.count(): if kafka_available(): with session_guard(select_query.session): batch_events = _delete_host_db_records(select_query, chunk_size, identity, interrupt, control_rule) - _send_delete_messages_for_batch(batch_events, event_producer, notification_event_producer) + _send_delete_messages_for_batch( + batch_events, event_producer, notification_event_producer, initiated_by_frontend + ) # yield the items in batch_events yield from batch_events diff --git a/tests/helpers/mq_utils.py b/tests/helpers/mq_utils.py index 65e101ab7..5cb0ef82b 100644 --- a/tests/helpers/mq_utils.py +++ b/tests/helpers/mq_utils.py @@ -104,7 +104,13 @@ def assert_mq_host_data(actual_id, actual_event, expected_results, host_keys_to_ def assert_delete_event_is_valid( - event_producer, host, timestamp, expected_request_id=None, expected_metadata=None, identity=USER_IDENTITY + event_producer, + host, + timestamp, + expected_request_id=None, + expected_metadata=None, + identity=USER_IDENTITY, + initiated_by_frontend=False, ): event = json.loads(event_producer.event) @@ -118,6 +124,8 @@ def assert_delete_event_is_valid( "org_id", "insights_id", "request_id", + "subscription_manager_id", + "initiated_by_frontend", "platform_metadata", "metadata", } @@ -129,6 +137,11 @@ def assert_delete_event_is_valid( assert host.canonical_facts.get("insights_id") == event["insights_id"] + assert event["initiated_by_frontend"] is initiated_by_frontend + + if initiated_by_frontend: + assert event["subscription_manager_id"] is not None + assert event_producer.key == str(host.id) assert event_producer.headers == expected_headers( "delete", diff --git a/tests/test_api_hosts_delete.py b/tests/test_api_hosts_delete.py index d93638b9b..5a1a95ff6 100644 --- a/tests/test_api_hosts_delete.py +++ b/tests/test_api_hosts_delete.py @@ -614,11 +614,32 @@ def test_log_create_delete( assert caplog.records[0].system_profile == "{}" +def test_delete_with_ui_host( + db_create_host, api_delete_host, event_datetime_mock, event_producer_mock, notification_event_producer_mock +): + host = db_create_host(extra_data={"canonical_facts": {"subscription_manager_id": generate_uuid()}}) + headers = {"x-rh-frontend-origin": "hcc"} + + response_status, _ = api_delete_host(host.id, extra_headers=headers) + + assert_response_status(response_status, expected_status=200) + + assert_delete_event_is_valid( + event_producer=event_producer_mock, host=host, timestamp=event_datetime_mock, initiated_by_frontend=True + ) + + class DeleteHostsMock: @classmethod - def create_mock(cls, hosts_ids_to_delete): + def create_mock(cls, hosts_ids_to_delete, initiated_by_frontend=False): def _constructor( - select_query, event_producer, notification_event_producer, chunk_size, identity=None, control_rule=None + select_query, + event_producer, + notification_event_producer, + chunk_size, + identity=None, + control_rule=None, + initiated_by_frontend=initiated_by_frontend, ): return cls( hosts_ids_to_delete, @@ -628,6 +649,7 @@ def _constructor( chunk_size, identity=identity, control_rule=control_rule, + initiated_by_frontend=initiated_by_frontend, ) return _constructor @@ -641,6 +663,7 @@ def __init__( chunk_size, identity=None, control_rule=None, + initiated_by_frontend=False, ): self.host_ids_to_delete = host_ids_to_delete self.original_query = delete_hosts( @@ -650,6 +673,7 @@ def __init__( chunk_size, identity=identity, control_rule=control_rule, + initiated_by_frontend=initiated_by_frontend, ) def __getattr__(self, item):