Skip to content

feat(rest): add quota management endpoint#748

Merged
tiborsimko merged 1 commit intoreanahub:masterfrom
CameronMcClymont:add-quota-endpoints
Apr 21, 2026
Merged

feat(rest): add quota management endpoint#748
tiborsimko merged 1 commit intoreanahub:masterfrom
CameronMcClymont:add-quota-endpoints

Conversation

@CameronMcClymont
Copy link
Copy Markdown
Member

@CameronMcClymont CameronMcClymont commented Sep 25, 2025

To use the endpoints, the secret REANA_ADMIN_QUOTA_MANAGER secret set in your Helm values.yaml under components.reana_server.environment must be included in the X-Quota-Management-Secret header of each request.

GET user quota info

Other query params:

- name: user_id
  description: Get the quota limit by user ID (mutually exclusive with `email` and `user_access_token`)
- name: email
  description: Get the quota limit by user email (mutually exclusive with `user_id` and `user_access_token`)
- name: user_access_token
  description: Get the quota limit by user access token (mutually exclusive with `user_id` and `email`)
- name: resource_type
  description: The type of resource

POST user quota limit

Body format:

user_id:
  type: string
  description: ID of the target user (mutually exclusive with `email`)
email:
  type: string
  description: Email of the target user (mutually exclusive with `user_id`)
resource_type:
  type: string
  description: Resource type to set
limit:
  type: integer
  description: Raw quota limit to set

Examples

Fetch CPU quota info for user with email '[email protected]':

GET /[email protected]&resource_type=cpu

X-Quota-Management-Secret your_secret

Set disk quota limit for user with ID 'abcde':

POST /quota

X-Quota-Management-Secret your_secret

{
    "user_id": "abcde",
    "limit": 12345678,
    "resource_type": "disk"
}

Closes #747

@CameronMcClymont CameronMcClymont self-assigned this Sep 25, 2025
CameronMcClymont added a commit to CameronMcClymont/reana-server that referenced this pull request Sep 25, 2025
CameronMcClymont added a commit to CameronMcClymont/reana-server that referenced this pull request Sep 25, 2025
CameronMcClymont added a commit to CameronMcClymont/reana-server that referenced this pull request Sep 25, 2025
@codecov
Copy link
Copy Markdown

codecov Bot commented Sep 25, 2025

Codecov Report

❌ Patch coverage is 19.08397% with 106 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.28%. Comparing base (3248aec) to head (37fe2b9).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
reana_server/rest/quota.py 23.75% 61 Missing ⚠️
reana_server/utils.py 6.81% 41 Missing ⚠️
reana_server/reana_admin/cli.py 0.00% 4 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #748      +/-   ##
==========================================
- Coverage   60.19%   59.28%   -0.91%     
==========================================
  Files          32       33       +1     
  Lines        3693     3790      +97     
==========================================
+ Hits         2223     2247      +24     
- Misses       1470     1543      +73     
Files with missing lines Coverage Δ
reana_server/config.py 86.80% <100.00%> (+0.11%) ⬆️
reana_server/factory.py 100.00% <100.00%> (ø)
reana_server/reana_admin/cli.py 53.84% <0.00%> (+3.14%) ⬆️
reana_server/utils.py 53.16% <6.81%> (-5.44%) ⬇️
reana_server/rest/quota.py 23.75% <23.75%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

CameronMcClymont added a commit to CameronMcClymont/reana-server that referenced this pull request Sep 26, 2025
@CameronMcClymont
Copy link
Copy Markdown
Member Author

I haven't seen these modules/reana-db and modules/reana-commons issues in the CI before. Any ideas how to fix?

@michaelbuchar
Copy link
Copy Markdown

michaelbuchar commented Oct 22, 2025

I haven't seen these modules/reana-db and modules/reana-commons issues in the CI before. Any ideas how to fix?

You added these two packages as a part of your PR, I would suggest removing them so they don't interfere with the docker build and manifest.

CameronMcClymont added a commit to CameronMcClymont/reana-server that referenced this pull request Oct 23, 2025
@CameronMcClymont
Copy link
Copy Markdown
Member Author

CameronMcClymont commented Oct 23, 2025

I haven't seen these modules/reana-db and modules/reana-commons issues in the CI before. Any ideas how to fix?

You added these two packages as a part of your PR, I would suggest removing them so they don't interfere with the docker build and manifest.

Ah weird, not sure how those got committed. Fixed now. Doesn't it make sense for these to be in the .gitignore in all the REANA repos?

CameronMcClymont added a commit to CameronMcClymont/reana-server that referenced this pull request Oct 23, 2025
@michaelbuchar
Copy link
Copy Markdown

@CameronMcClymont is this a valid URL: https://localhost:30443/api/quota?

I am getting a 404 error, would that be expected?

{
  "message": "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.",
  "status": 404
}

CameronMcClymont added a commit to CameronMcClymont/reana-server that referenced this pull request Nov 6, 2025
CameronMcClymont added a commit to CameronMcClymont/reana-server that referenced this pull request Nov 6, 2025
Comment thread docs/openapi.json
Comment thread docs/openapi.json
Comment thread docs/openapi.json Outdated
Comment thread docs/openapi.json
Comment thread reana_server/rest/quota.py
Comment thread docs/openapi.json
Comment thread docs/openapi.json Outdated
Comment thread reana_server/rest/quota.py Outdated
Comment thread docs/openapi.json
Comment thread reana_server/rest/quota.py Outdated
CameronMcClymont added a commit to CameronMcClymont/reana-server that referenced this pull request Nov 11, 2025
CameronMcClymont added a commit to CameronMcClymont/reana-server that referenced this pull request Nov 11, 2025
michaelbuchar
michaelbuchar previously approved these changes Nov 11, 2025
CameronMcClymont added a commit to CameronMcClymont/reana-server that referenced this pull request Nov 28, 2025
michaelbuchar pushed a commit to CameronMcClymont/reana-server that referenced this pull request Dec 2, 2025
Comment thread reana_server/utils.py
return value


def _set_quota_limit(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In addition to quota limits, it appeared desirable to allow nullifying usage count. This is useful for deployments where CPU credits are periodically renewed.

(This may need some updates on the backend side so that CPU quotas won't be re-added later; let's discuss IRL.)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Let's create a new PR set for this and other EEN dependencies?

michaelbuchar pushed a commit to CameronMcClymont/reana-server that referenced this pull request Mar 3, 2026
michaelbuchar pushed a commit to CameronMcClymont/reana-server that referenced this pull request Mar 3, 2026
@michaelbuchar
Copy link
Copy Markdown

michaelbuchar commented Mar 3, 2026

The docs-sphinx and python-tests checks are fixed in another commit: 322f5fa so that PR has to come before this one.

michaelbuchar pushed a commit to CameronMcClymont/reana-server that referenced this pull request Apr 17, 2026
michaelbuchar pushed a commit to CameronMcClymont/reana-server that referenced this pull request Apr 17, 2026
michaelbuchar pushed a commit to CameronMcClymont/reana-server that referenced this pull request Apr 17, 2026
michaelbuchar pushed a commit to CameronMcClymont/reana-server that referenced this pull request Apr 17, 2026
Comment thread reana_server/reana_admin/cli.py Outdated
)
sys.exit(1)
msg, status_code, fatal = _set_quota_limit(
resource_type, resource_name, limit, emails=emails
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

AI review

Bug: Wrong argument order in _set_quota_limit call.

The positional arguments here don't match the function signature _set_quota_limit(limit, resource_type=None, resource_name=None, emails=None, user_ids=None). As written, limit receives the resource_type string, resource_type receives resource_name, and resource_name receives limit.

Suggested fix — use keyword arguments:

msg, status_code, fatal = _set_quota_limit(
    limit, resource_type=resource_type, resource_name=resource_name, emails=emails
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks, fixed!

Comment thread reana_server/rest/quota.py Outdated

# Check if secret is provided and matches the one in the config
secret = request.headers.get("X-Quota-Management-Secret")
if secret != REANA_QUOTA_MANAGEMENT_SECRET:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

AI review

Security: Secret comparison should be constant-time.

Using != for secret comparison is vulnerable to timing attacks. Consider using secrets.compare_digest() instead:

import secrets

secret = request.headers.get("X-Quota-Management-Secret", "")
if not secrets.compare_digest(secret, REANA_QUOTA_MANAGEMENT_SECRET):
    return jsonify(message="Unauthorized"), 401

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fixed!

description: Data required to set quota limits (exactly one of `user_id` or `email` must be provided).
required: true
schema:
type: object
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

AI review

OpenAPI spec: required fields missing from POST body schema.

The marshmallow SetQuotaLimitBodySchema marks resource_type and limit as required=True, but the OpenAPI schema here doesn't declare a required array. Swagger UI and generated clients will treat all fields as optional.

Suggested fix — add required to the schema:

          schema:
            type: object
            required:
              - resource_type
              - limit
            properties:
              ...

(The same fix should be applied to the corresponding spec in docs/openapi.json and reana-commons.)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fixed

Comment thread reana_server/utils.py
True,
)
else:
resource = available_resources[0]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

AI review

Bug: IndexError if no Resource rows exist for a valid resource_type.

If resource_type is a valid ResourceType member but no matching Resource rows exist in the DB, available_resources is []. The if available_resources and len(...) > 1 condition is False, so the else branch runs available_resources[0] which raises IndexError. This was pre-existing in the CLI code, but is now reachable via the new REST API where it would surface as an unhandled 500.

Suggested fix:

elif resource_type in ResourceType._member_names_:
    available_resources = Resource.query.filter_by(type_=resource_type).all()
    if len(available_resources) > 1:
        return (
            f"ERROR: There are more than one `{resource_type}` resource. ...",
            400,
            True,
        )
    elif len(available_resources) == 1:
        resource = available_resources[0]
    # else: resource stays None, handled by the `if not resource:` block below

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fixed

@tiborsimko
Copy link
Copy Markdown
Member

AI review

No unit tests for new REST endpoints and utility function.

The new reana_server/rest/quota.py (GET and POST endpoints) and the _set_quota_limit utility in reana_server/utils.py have no corresponding tests. Codecov reports 105 lines missing coverage: quota.py at 22.78% patch coverage, utils.py at 6.97% patch coverage.

Given the amount of validation logic (mutually exclusive parameters, resource type checks, secret verification, error handling), unit tests would help catch issues like the argument-order bug noted in the inline comments.

Comment thread reana_server/rest/quota.py Outdated

def _check_quota_management_secret() -> tuple[Response, int] | None:
if not REANA_QUOTA_MANAGEMENT_SECRET:
return jsonify(message="Quota functionality is not enabled"), 403
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

AI review

Misleading error message: "Quota functionality is not enabled".

The REANA quota system itself is not disabled — only the new REST management endpoints are not configured (because REANA_QUOTA_MANAGEMENT_SECRET is empty). The current message could confuse callers into thinking quotas don't exist on this cluster.

Suggested fix — use a more precise message, e.g.:

return jsonify(message="Quota management endpoint is not configured."), 403

The same message appears in the OpenAPI docstrings in this file (lines 178 and 353) and in docs/openapi.json (lines 1252 and 1405). All occurrences should be updated consistently.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fixed!

@michaelbuchar
Copy link
Copy Markdown

michaelbuchar commented Apr 21, 2026

AI review

No unit tests for new REST endpoints and utility function.

The new reana_server/rest/quota.py (GET and POST endpoints) and the _set_quota_limit utility in reana_server/utils.py have no corresponding tests. Codecov reports 105 lines missing coverage: quota.py at 22.78% patch coverage, utils.py at 6.97% patch coverage.

Given the amount of validation logic (mutually exclusive parameters, resource type checks, secret verification, error handling), unit tests would help catch issues like the argument-order bug noted in the inline comments.

Integration tests performed manually

@tiborsimko
Copy link
Copy Markdown
Member

AI review

Commit headline suggestion:

feat(rest): add quota management endpoint (#748)

(Singular "endpoint" — it's one /api/quota route with GET and POST methods.)

@michaelbuchar michaelbuchar changed the title feat(rest): add quota management endpoints feat(rest): add quota management endpoint Apr 21, 2026
@tiborsimko tiborsimko merged commit 37fe2b9 into reanahub:master Apr 21, 2026
19 checks passed
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.

rest: add optional quota management endpoints

3 participants