feat(rest): add quota management endpoint#748
Conversation
63f0a43 to
32e128c
Compare
32e128c to
ba2e8d3
Compare
ba2e8d3 to
444f898
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ 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
🚀 New features to boost your workflow:
|
444f898 to
28dac67
Compare
|
I haven't seen these |
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. |
28dac67 to
9c3fccb
Compare
Ah weird, not sure how those got committed. Fixed now. Doesn't it make sense for these to be in the |
9c3fccb to
bcce9d9
Compare
|
@CameronMcClymont is this a valid URL: I am getting a 404 error, would that be expected? |
bcce9d9 to
3416cb9
Compare
3416cb9 to
f55c1f0
Compare
f55c1f0 to
e9fa570
Compare
e9fa570 to
fdbaba2
Compare
fdbaba2 to
80dfff0
Compare
80dfff0 to
b729ede
Compare
| return value | ||
|
|
||
|
|
||
| def _set_quota_limit( |
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
Let's create a new PR set for this and other EEN dependencies?
b729ede to
070b499
Compare
070b499 to
23e7f58
Compare
|
The docs-sphinx and python-tests checks are fixed in another commit: 322f5fa so that PR has to come before this one. |
23e7f58 to
aba72c9
Compare
aba72c9 to
cab080c
Compare
cab080c to
aa3acb6
Compare
aa3acb6 to
0b9e8ee
Compare
| ) | ||
| sys.exit(1) | ||
| msg, status_code, fatal = _set_quota_limit( | ||
| resource_type, resource_name, limit, emails=emails |
There was a problem hiding this comment.
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
)|
|
||
| # 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: |
There was a problem hiding this comment.
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| description: Data required to set quota limits (exactly one of `user_id` or `email` must be provided). | ||
| required: true | ||
| schema: | ||
| type: object |
There was a problem hiding this comment.
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.)
| True, | ||
| ) | ||
| else: | ||
| resource = available_resources[0] |
There was a problem hiding this comment.
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
AI reviewNo unit tests for new REST endpoints and utility function. The new 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. |
|
|
||
| def _check_quota_management_secret() -> tuple[Response, int] | None: | ||
| if not REANA_QUOTA_MANAGEMENT_SECRET: | ||
| return jsonify(message="Quota functionality is not enabled"), 403 |
There was a problem hiding this comment.
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."), 403The 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.
Integration tests performed manually |
AI reviewCommit headline suggestion: (Singular "endpoint" — it's one |
0b9e8ee to
37fe2b9
Compare
To use the endpoints, the secret
REANA_ADMIN_QUOTA_MANAGERsecret set in your Helmvalues.yamlundercomponents.reana_server.environmentmust be included in theX-Quota-Management-Secretheader of each request.GETuser quota infoOther query params:
POSTuser quota limitBody format:
Examples
Fetch CPU quota info for user with email '[email protected]':
Set disk quota limit for user with ID 'abcde':
Closes #747