Skip to content

feat(RHINENG-24544): Add feature flag to force single-resource checks for Kessel bulk operations#3695

Open
thearifismail wants to merge 5 commits intomasterfrom
RHINENG-24544-code
Open

feat(RHINENG-24544): Add feature flag to force single-resource checks for Kessel bulk operations#3695
thearifismail wants to merge 5 commits intomasterfrom
RHINENG-24544-code

Conversation

@thearifismail
Copy link
Contributor

@thearifismail thearifismail commented Mar 4, 2026

Overview

This PR is being created to address RHINENG-24544.
Here is the 2nd PR. Since this is a temporary change, I put the test in a separate file

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

Add a feature-flag-controlled path to force single-resource authorization checks for Kessel bulk operations and add comprehensive tests around bulk-check behavior and edge cases.

New Features:

  • Introduce a feature flag that switches Kessel bulk authorization checks to per-resource single checks when enabled.

Tests:

  • Add unit and integration tests covering bulk vs single-resource behavior under the feature flag, including unauthorized scenarios, logging, and edge cases like empty and single-ID inputs.

@thearifismail thearifismail requested a review from a team as a code owner March 4, 2026 19:27
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Mar 4, 2026

Reviewer's Guide

Introduces a feature flag-controlled path in Kessel bulk authorization checks that can force per-resource checks instead of using the gRPC bulk API, and adds comprehensive unit tests around bulk vs single-resource behavior and edge cases.

Sequence diagram for Kessel bulk check with feature-flagged single-resource path

sequenceDiagram
    actor Service
    participant KesselAuthorizer
    participant FeatureFlags
    participant Logger
    participant KesselSingleCheck
    participant KesselBulkApi

    Service->>KesselAuthorizer: _check_bulk_resources(subject_ref, permission, resource_ids)
    KesselAuthorizer->>KesselAuthorizer: validate resource_ids not empty

    KesselAuthorizer->>FeatureFlags: get_flag_value(FLAG_INVENTORY_KESSEL_FORCE_SINGLE_CHECKS_FOR_BULK)
    FeatureFlags-->>KesselAuthorizer: flag_value

    alt flag_value is true
        KesselAuthorizer->>Logger: debug(Using single-resource checks...)
        loop for each resource_id
            KesselAuthorizer->>KesselSingleCheck: _check_single_resource(subject_ref, permission, resource_id)
            KesselSingleCheck-->>KesselAuthorizer: is_authorized
            KesselAuthorizer->>KesselAuthorizer: collect unauthorized_ids
        end
        KesselAuthorizer-->>Service: (len(unauthorized_ids) == 0, unauthorized_ids)
    else flag_value is false
        KesselAuthorizer->>KesselBulkApi: CheckBulkRequest(subject_ref, permission, resource_ids)
        KesselBulkApi-->>KesselAuthorizer: (all_authorized, unauthorized_ids)
        KesselAuthorizer-->>Service: (all_authorized, unauthorized_ids)
    end
Loading

Class diagram for Kessel bulk vs single-resource check with feature flag

classDiagram
    class KesselAuthorizer {
        +bool _check_bulk_resources(subject_ref, permission, resource_ids)
        +bool _check_single_resource(subject_ref, permission, resource_id)
    }

    class FeatureFlags {
        +bool get_flag_value(flag_name)
        <<constant>> FLAG_INVENTORY_KESSEL_FORCE_SINGLE_CHECKS_FOR_BULK
    }

    class Logger {
        +debug(message, extra)
    }

    class KesselBulkApi {
        +tuple check_bulk(subject_ref, permission, resource_ids)
    }

    KesselAuthorizer --> FeatureFlags : uses
    KesselAuthorizer --> Logger : logs_to
    KesselAuthorizer --> KesselBulkApi : invokes_for_bulk_checks
Loading

File-Level Changes

Change Details Files
Add feature-flag-controlled fallback from bulk authorization checks to per-resource checks in Kessel.
  • Import the inventory Kessel bulk single-checks feature flag constant and flag-reading helper.
  • In _check_bulk_resources, short-circuit to a loop that calls _check_single_resource for each resource_id when the feature flag is enabled.
  • Log a debug message indicating that single-resource checks are being used and include the number of resource IDs.
  • Preserve existing bulk CheckBulk request construction and behavior when the flag is disabled.
lib/kessel.py
Add unit tests for Kessel bulk check behavior with and without the feature flag, including integration with check() and edge cases.
  • Create fixtures for mock Kessel configuration, Kessel client, Identity, KesselPermission, and a real SubjectReference protobuf.
  • Test that _check_bulk_resources calls the bulk CheckBulk API when the feature flag is disabled and returns correct authorization results.
  • Test that _check_bulk_resources calls _check_single_resource per resource when the feature flag is enabled, including cases with some or all resources unauthorized and verifying logging.
  • Test that check() delegates to bulk vs single-resource paths appropriately depending on the number of resource IDs and the feature flag state.
  • Test edge cases such as empty resource_ids raising ValueError and single-resource checks bypassing bulk logic regardless of the feature flag.
tests/test_lib_kessel.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 Mar 4, 2026

SC Environment Impact Assessment

Overall Impact: 🔴 CRITICAL

View full report

Summary

  • Total Issues: 4
  • 🔴 Critical: 2
  • 🟢 Low: 2

Detailed Findings

🔴 CRITICAL Impact

Kessel integration change detected

  • File: lib/kessel.py
  • Category: kessel_integration
  • Details:
    • Found KESSEL in lib/kessel.py at line 21
    • Found KESSEL in lib/kessel.py at line 252
  • Recommendation: ⚠️ CRITICAL: Kessel is not be available in SC Environment. All Kessel-dependent features must have fallback behavior.

Kessel integration change detected

  • File: tests/test_lib_kessel.py
  • Category: kessel_integration
  • Details:
    • Found kessel in tests/test_lib_kessel.py at line 2
    • Found Kessel in tests/test_lib_kessel.py at line 2
    • Found kessel in tests/test_lib_kessel.py at line 11
    • Found kessel.inventory in tests/test_lib_kessel.py at line 11
    • Found kessel in tests/test_lib_kessel.py at line 12
  • Recommendation: ⚠️ CRITICAL: Kessel is not be available in SC Environment. All Kessel-dependent features must have fallback behavior.

🟢 LOW Impact

Feature flag change detected

  • File: lib/kessel.py
  • Category: feature_flags
  • Details:
    • Found feature_flags import FLAG in lib/kessel.py at line 21
    • Found feature_flags import get_flag in lib/kessel.py at line 22
    • Found feature flag in lib/kessel.py at line 251
    • Found feature flag in lib/kessel.py at line 254
  • Recommendation: Verify feature flags are properly configured for SC Environment. Test bypass options for services not available in SC Environment.

Feature flag change detected

  • File: tests/test_lib_kessel.py
  • Category: feature_flags
  • Details:
    • Found feature flag in tests/test_lib_kessel.py at line 4
    • Found FeatureFlag in tests/test_lib_kessel.py at line 71
    • Found feature flag in tests/test_lib_kessel.py at line 72
    • Found feature flag in tests/test_lib_kessel.py at line 78
    • Found feature flag in tests/test_lib_kessel.py at line 82
  • Recommendation: Verify feature flags are properly configured for SC Environment. Test bypass options for services not available in SC Environment.

Required Actions

  • Review all findings above
  • Verify SC Environment compatibility for all detected changes
  • Update deployment documentation if needed
  • Coordinate with ROSA Core team or deployment timeline

This assessment was automatically generated. Please review carefully and consult with the ROSA Core team for critical/high impact changes.

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 1 issue, and left some high level feedback:

  • Several tests (e.g. in TestCheckBulkResourcesFeatureFlag) patch _build_subject_reference even though _check_bulk_resources already receives a subject_ref argument and does not call _build_subject_reference, so those patches can be removed to simplify the test setup.
  • In the kessel_client fixture you patch ClientBuilder but never use it after bypassing Kessel.__init__; you can drop that patch to reduce noise and make the intended construction behavior clearer.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Several tests (e.g. in `TestCheckBulkResourcesFeatureFlag`) patch `_build_subject_reference` even though `_check_bulk_resources` already receives a `subject_ref` argument and does not call `_build_subject_reference`, so those patches can be removed to simplify the test setup.
- In the `kessel_client` fixture you patch `ClientBuilder` but never use it after bypassing `Kessel.__init__`; you can drop that patch to reduce noise and make the intended construction behavior clearer.

## Individual Comments

### Comment 1
<location path="tests/test_lib_kessel.py" line_range="278-115" />
<code_context>
+    def test_check_with_multiple_ids_flag_enabled(self, kessel_client, test_identity, test_permission, mocker):
</code_context>
<issue_to_address>
**suggestion (testing):** Assert that the bulk API is not invoked when the feature flag forces single-resource checks

This test only verifies that `_check_single_resource` is used with the flag enabled, but doesn’t confirm that `inventory_svc.CheckBulk` is skipped. Please add an assertion like `assert kessel_client.inventory_svc.CheckBulk.call_count == 0` (or `assert_not_called()`) so the test guarantees the bulk path is not used when the feature flag is on.

Suggested implementation:

```python
    def test_check_with_multiple_ids_flag_enabled(self, kessel_client, test_identity, test_permission, mocker):
        """
        Test that check() with multiple IDs uses _check_bulk_resources which respects the feature flag.

        JIRA: RHINENG-24544
        """
        # Mock feature flag as enabled
        mocker.patch("lib.kessel.get_flag_value", return_value=True)

        # Mock _build_subject_reference
        mock_subject_ref = Mock()
        kessel_client._build_subject_reference = mocker.Mock(return_value=mock_subject_ref)

        # Mock single-resource check implementation
        kessel_client._check_single_resource = mocker.Mock(return_value=True)

        # Ensure inventory_svc.CheckBulk is a mock so we can assert it is not used
        kessel_client.inventory_svc.CheckBulk = mocker.Mock()

        resource_ids = ["res-1", "res-2", "res-3"]

        # Act: call check() with multiple IDs
        result = kessel_client.check(
            identity=test_identity,
            resource_type="test-resource-type",
            resource_ids=resource_ids,
            permission=test_permission,
        )

        # Assert: single-resource path is used for each ID
        assert kessel_client._check_single_resource.call_count == len(resource_ids)

        # Assert: bulk API is NOT invoked when feature flag forces single-resource checks
        kessel_client.inventory_svc.CheckBulk.assert_not_called()

        # Optionally assert on result shape/value if check() has a defined aggregate behavior
        # (e.g. boolean aggregate or per-resource dict); adapt as needed:
        assert result

```

Depending on the existing `kessel_client` fixture and `check()` contract in your codebase, you may want/need to:

1. Adjust the `resource_type="test-resource-type"` argument to match whatever resource type your other tests use (e.g. a `test_resource_type` fixture or a specific string constant).
2. Refine the `result` assertion to match the actual return type of `check()` when given multiple `resource_ids` (it might be a boolean, a mapping of `resource_id -> bool`, etc.). Replace `assert result` with the appropriate expectation.
3. If `kessel_client.inventory_svc.CheckBulk` is already a mock in the fixture, you can drop the explicit assignment here and just keep the `assert_not_called()` assertion.
</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.

mocker.patch("lib.kessel.get_flag_value", return_value=True)

# Mock _build_subject_reference
mocker.patch.object(kessel_client, "_build_subject_reference", return_value=mock_subject_ref)
Copy link
Member

Choose a reason for hiding this comment

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

This mock can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

mocker.patch("lib.kessel.get_flag_value", return_value=True)

# Mock _build_subject_reference
mocker.patch.object(kessel_client, "_build_subject_reference", return_value=mock_subject_ref)
Copy link
Member

Choose a reason for hiding this comment

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

This mock can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed!

mocker.patch("lib.kessel.get_flag_value", return_value=True)

# Mock _build_subject_reference
mocker.patch.object(kessel_client, "_build_subject_reference", return_value=mock_subject_ref)
Copy link
Member

Choose a reason for hiding this comment

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

This mock can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed!

mocker.patch("lib.kessel.get_flag_value", return_value=True)

# Mock _build_subject_reference
mocker.patch.object(kessel_client, "_build_subject_reference", return_value=mock_subject_ref)
Copy link
Member

Choose a reason for hiding this comment

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

This mock can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed!



@pytest.fixture
def mock_config():
Copy link
Member

Choose a reason for hiding this comment

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

This fixture is not used. Kessel init in the kessel_client fixture is patched to a no-op lambda that discards the config, so mock_config is never actually read. This can be removed and Kessel(mock_config) simplified to Kessel(None).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed and gone!

@thearifismail
Copy link
Contributor Author

/retest

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.

2 participants