Skip to content

chore(RHINENG-24199): Allow grouped hosts to move to other groups#3592

Open
johnsonm325 wants to merge 1 commit intoRedHatInsights:masterfrom
johnsonm325:rhineng-24199
Open

chore(RHINENG-24199): Allow grouped hosts to move to other groups#3592
johnsonm325 wants to merge 1 commit intoRedHatInsights:masterfrom
johnsonm325:rhineng-24199

Conversation

@johnsonm325
Copy link

@johnsonm325 johnsonm325 commented Feb 17, 2026

Currently, HBI API returns HTTP 400 when trying to add grouped hosts to a different group. The response has this detail: "The following subset of hosts are already associated with another group: []."

This should now be allowed; the host should be removed from its old group and added to the specified one.

Overview

This PR is being created to address RHINENG-xxxx.
(A description of your PR's changes, along with why/context to the PR, goes here.)

PR Checklist

  • Keep PR title short, ideally under 72 characters
  • Descriptive comments provided in complex code blocks
  • Include raw query examples in the PR description, if adding/modifying SQL query
  • Tests: validate optimal/expected output
  • Tests: validate exceptions and failure scenarios
  • Tests: edge cases
  • Recovers or fails gracefully during potential resource outages (e.g. DB, Kafka)
  • Uses type hinting, if convenient
  • Documentation, if this PR changes the way other services interact with host inventory
  • Links to related PRs

Secure Coding Practices Documentation Reference

You can find documentation on this checklist here.

Secure Coding Checklist

  • Input Validation
  • Output Encoding
  • Authentication and Password Management
  • Session Management
  • Access Control
  • Cryptographic Practices
  • Error Handling and Logging
  • Data Protection
  • Communication Security
  • System Configuration
  • Database Security
  • File Management
  • Memory Management
  • General Coding Practices

Summary by Sourcery

Allow hosts already associated with a group to be reassigned to a different group instead of returning a 400 error.

Bug Fixes:

  • Fix host group assignment so that adding hosts already in another group moves them to the target group rather than rejecting the request.

Tests:

  • Update and extend host group API tests to verify that hosts can be moved between groups, their memberships are updated correctly, and events are emitted for each change.

@johnsonm325 johnsonm325 requested a review from a team as a code owner February 17, 2026 21:56
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 17, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

This PR removes validation that previously blocked moving hosts between non-ungrouped groups, and updates tests to confirm that adding hosts already associated with another group now results in them being moved to the target group with corresponding events emitted.

Sequence diagram for adding hosts to a group with reassignment allowed

sequenceDiagram
    actor Client
    participant API as HBI_API
    participant Repo as GroupRepository
    participant DB as Database
    participant Events as EventProducer

    Client->>API: POST /groups/{group_id}/hosts [host_id_list]
    API->>Repo: _add_hosts_to_group(group_id, host_id_list, org_id)

    Repo->>DB: Validate hosts exist and belong to org_id
    DB-->>Repo: Existing hosts for org_id

    note over Repo,DB: Previous validation blocking reassignment is removed

    Repo->>DB: Remove existing HostGroupAssoc rows for host_id_list (any group)
    DB-->>Repo: Existing associations removed

    Repo->>DB: Insert HostGroupAssoc rows for group_id and host_id_list
    DB-->>Repo: New associations committed

    Repo->>Events: Emit host_group_change events for reassigned hosts
    Events-->>Repo: Events accepted

    Repo-->>API: Success (updated host group associations)
    API-->>Client: 200 OK (hosts now in target group)
Loading

Class diagram for host group association and repository functions after change

classDiagram
    class GroupRepository {
        +validate_add_host_list_to_group_for_group_create(host_id_list, group_name, org_id)
        +validate_add_host_list_to_group(host_id_list, group_id, org_id)
        +_add_hosts_to_group(group_id, host_id_list, org_id)
    }

    class HostGroupAssoc {
        +host_id: uuid
        +group_id: uuid
        +created_on: datetime
        +updated_on: datetime
    }

    class Group {
        +id: uuid
        +name: string
        +org_id: string
        +ungrouped: bool
    }

    class InventoryException {
        +title: string
        +detail: string
    }

    GroupRepository --> HostGroupAssoc : manages associations
    HostGroupAssoc --> Group : many_to_one

    GroupRepository ..> InventoryException : raises on invalid host_ids or org_id

    class EventProducer {
        +emit_host_group_change(host_id, old_group_id, new_group_id)
    }

    GroupRepository ..> EventProducer : emits events on reassignment
Loading

File-Level Changes

Change Details Files
Allow hosts already associated with a non-ungrouped group to be added to another group instead of raising a 400 error.
  • Removed validation that queried HostGroupAssoc joined with Group to detect hosts already associated with another non-ungrouped group during group creation.
  • Removed validation that rejected hosts already associated with a different non-ungrouped group when adding hosts to an existing group.
lib/group_repository.py
Adjust host-group API behavior test to assert successful movement of hosts between groups and event emission.
  • Updated test to document new behavior that adding hosts already in other groups should move them to the target group instead of failing.
  • Changed test setup comments and assertions to validate that all three hosts end up in the target group, the source group becomes empty, and three update events are emitted.
  • Removed previous expectations of a 400 response, rollback of changes, and zero events.
tests/test_api_host_group.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions
Copy link
Contributor

github-actions bot commented Feb 17, 2026

SC Environment Impact Assessment

Overall Impact:NONE

No SC Environment-specific impacts detected in this PR.

What was checked

This PR was automatically scanned for:

  • Database migrations
  • ClowdApp configuration changes
  • Kessel integration changes
  • AWS service integrations (S3, RDS, ElastiCache)
  • Kafka topic changes
  • Secrets management changes
  • External dependencies

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • Since the API semantics have changed from "disallow if already grouped" to "move to the new group", consider adding a short comment near the group-association logic explaining that existing associations are intentionally overwritten so future maintainers don’t reintroduce the old validation pattern.
  • In the updated test_add_associated_host_to_different_group, you may want to assert the specific host IDs present in group1 and absent from group2 rather than just lengths, to better document and lock in the exact move semantics.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Since the API semantics have changed from "disallow if already grouped" to "move to the new group", consider adding a short comment near the group-association logic explaining that existing associations are intentionally overwritten so future maintainers don’t reintroduce the old validation pattern.
- In the updated `test_add_associated_host_to_different_group`, you may want to assert the specific host IDs present in `group1` and absent from `group2` rather than just lengths, to better document and lock in the exact move semantics.

## Individual Comments

### Comment 1
<location> `tests/test_api_host_group.py:197` </location>
<code_context>
+def test_add_associated_host_to_different_group(
</code_context>

<issue_to_address>
**suggestion (testing):** Add an assertion that host-group associations for the old groups are actually removed, not just that counts change

Currently the test only checks the lengths of `db_get_hosts_for_group(group1_id)` and `db_get_hosts_for_group(group2_id)`. To better verify the move semantics, please assert the exact host IDs for each group and that no hosts remain associated with the old group(s). For example, check that `sorted(h.id for h in db_get_hosts_for_group(group1_id)) == sorted(host_id_list)` and that `db_get_hosts_for_group(group2_id)` returns an empty list.
</issue_to_address>

### Comment 2
<location> `tests/test_api_host_group.py:196-197` </location>
<code_context>
+    # All 3 hosts should now be in group1; group2 should have 0
+    assert len(db_get_hosts_for_group(group1_id)) == 3
+    assert len(db_get_hosts_for_group(group2_id)) == 0
+    # Update events should have been produced for the moved/added hosts
+    assert event_producer.write_event.call_count == 3


</code_context>

<issue_to_address>
**suggestion (testing):** Strengthen event assertions by validating payload contents or per-host calls

Right now the test only verifies the number of calls to `write_event`. To better validate behavior, assert which hosts and event details were emitted by inspecting `event_producer.write_event.call_args_list`—e.g., confirm each call references a `host_id_list` entry and has the expected update (not failure) event metadata. This will make regressions in event contents easier to catch.

Suggested implementation:

```python
    # All 3 hosts should now be in group1; group2 should have 0
    assert len(db_get_hosts_for_group(group1_id)) == 3
    assert len(db_get_hosts_for_group(group2_id)) == 0

    # Update events should have been produced for the moved/added hosts
    calls = event_producer.write_event.call_args_list
    assert len(calls) == 3

    # Each emitted event should correspond to one of the affected hosts
    emitted_host_ids = {call.args[0]["host"]["id"] for call in calls}
    assert emitted_host_ids == set(host_id_list)

    # And each event should be a successful update (not a failure)
    for call in calls:
        event = call.args[0]
        assert event["type"] == "updated"
        assert event.get("status") != "failure"

```

If the actual structure of the event passed to `write_event` differs (e.g., the host id is under a different key, or the event type field is named something else, or the event is passed as a keyword argument instead of `args[0]`), adjust:

1. The `emitted_host_ids` comprehension to navigate to the real host id field.
2. The `event["type"]` and `event.get("status")` assertions to match the actual event schema (e.g., `event["event_type"]`, `event["metadata"]["result"]`, etc.).
3. If `write_event` receives multiple positional args, pick the correct index instead of `args[0]`, or switch to `call.kwargs[...]` if the event is passed by keyword.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@johnsonm325 johnsonm325 force-pushed the rhineng-24199 branch 4 times, most recently from aa4527e to ca62018 Compare February 24, 2026 14:02
@kruai
Copy link
Collaborator

kruai commented Feb 24, 2026

/retest

@johnsonm325 johnsonm325 force-pushed the rhineng-24199 branch 5 times, most recently from 987dc39 to b78b5cb Compare February 25, 2026 21:20
kruai
kruai previously approved these changes Feb 25, 2026
Copy link
Collaborator

@kruai kruai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Looks fine, just got a question.

for call in calls:
event = json.loads(call.args[0])
assert event["type"] == "updated"
assert event.get("status") != "failure"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In what scenario does it get set to "failure"?

Copy link
Contributor

@fstavela fstavela left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You will need to update IQE tests as well; currently, they expect the old behavior.

Currently, HBI API returns HTTP 400 when trying to add grouped hosts to a different group. The response has this detail: "The following subset of hosts are already associated with another group: []."

This should now be allowed; the host should be removed from its old group and added to the specified one.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants