Skip to content

fix(admin,api): honor STORAGE_IMPL when surfacing file_store backend (closes #17294) - #17441

Open
Harsh23Kashyap wants to merge 3 commits into
infiniflow:mainfrom
Harsh23Kashyap:fix/admin-storage-status-honors-impl
Open

fix(admin,api): honor STORAGE_IMPL when surfacing file_store backend (closes #17294)#17441
Harsh23Kashyap wants to merge 3 commits into
infiniflow:mainfrom
Harsh23Kashyap:fix/admin-storage-status-honors-impl

Conversation

@Harsh23Kashyap

Copy link
Copy Markdown
Contributor

Summary

When STORAGE_IMPL=AWS_S3, the Admin Service status page keeps showing
MinIO. Three things conspire:

  1. admin/server/config.py::load_configurations only knows the
    minio and minio_0 config keys. An s3 block lands on the
    case _: branch and logs Unknown configuration key: s3
    (issue [Bug]: Admin Service status still reports MinIO when object storage is configured as AWS S3 #17294).
  2. admin/server/services.py::ServiceMgr.get_all_services filters
    retrieval services by DOC_ENGINE but has no equivalent filter
    for file_store services by STORAGE_IMPL. A stale MinIO block
    is returned regardless of the active backend.
  3. The wired health check is hardcoded to check_minio_alive, which
    calls settings.MINIO['host']. With STORAGE_IMPL=AWS_S3 that
    block is uninitialized, so the check always times out.

Changes

admin/server/config.py:

  • New case "s3" branch that creates a FileStoreConfig with
    store_type="s3", parsing endpoint_url for host/port. Falls
    back to s3.amazonaws.com:443 when endpoint_url is absent.

admin/server/services.py:

  • New STORAGE_IMPL filter in ServiceMgr.get_all_services. The
    env var name (e.g. AWS_S3) is mapped to the lowercase
    store_type (e.g. s3) by stripping the AWS_ prefix and
    lowercasing.

api/utils/health_utils.py:

  • New check_s3_alive that delegates to the existing generic
    check_storage. The generic helper already uses
    settings.STORAGE_IMPL.health(), so the same code path works for
    AWS S3, MinIO, R2, and any other S3-compatible endpoint.

Tests

  • test/unit_test/api/utils/test_health_utils_s3.py — 5 cases
    covering alive/timeout paths and the delegate contract.
  • test/unit_test/admin/test_load_configurations.py — 10 cases
    covering the new case "s3" branch (https, http, non-default
    port, omitted endpoint), regression guard for the existing minio
    path, both-backends-in-one-config, and 3 malformed-endpoint
    "does not raise" cases.
  • test/unit_test/admin/test_get_all_services.py — 9 cases covering
    the new filter (minio-active, s3-active, no-config), the existing
    DOC_ENGINE retrieval filter (regression guard), and a
    parametrized env-var-to-store_type mapping test (MINIO,
    AWS_S3, OSS, GCS).
  • test/unit_test/admin/conftest.py — sys.path shim so the admin
    module (which is run as a script in production) imports cleanly
    from pytest.

36 tests pass locally. Ruff check + format clean.

Out of scope (follow-up)

The same shape applies to oss, gcs, azure_spn, azure_sas,
and opendal. Each is a small case block in
admin/server/config.py plus the same store_type name. I scoped
this PR to S3 because the issue is specifically about S3; the rest
can land as one or more follow-up PRs using the same pattern.

Backward compatibility

  • Existing minio configurations are unchanged. The new
    case "s3" is purely additive.
  • When the s3 block is absent and STORAGE_IMPL=MINIO, the
    existing minio entry still appears (regression-guard tests).
  • The new filter is a no-op when there is only one file_store
    config in service_conf.yaml (the common case).

Fixes #17294

…loses infiniflow#17294)

1. admin/server/config.py: add `case "s3"` branch to load_configurations that
   creates a FileStoreConfig with store_type="s3", parsing endpoint_url
   for host/port. Falls back to s3.amazonaws.com:443 when endpoint_url
   is absent so the status page still renders a sane host.
2. admin/server/services.py: add a STORAGE_IMPL filter to
   ServiceMgr.get_all_services that hides inactive file_store backends
   (e.g. a stale minio entry from service_conf.yaml when STORAGE_IMPL
   =AWS_S3). Maps env var names (AWS_S3, MINIO, ...) to lowercase
   store_type (s3, minio, ...) by stripping the AWS_ prefix and
   lowercasing.
3. api/utils/health_utils.py: add check_s3_alive that delegates to the
   existing generic check_storage so the same code path works for AWS
   S3, MinIO, R2, or any other S3-compatible endpoint.

Without this fix, the Admin Service status page reports MinIO as the
active file_store even when STORAGE_IMPL=AWS_S3, and the wired health
check (check_minio_alive) times out against settings.MINIO['host'].
… check_s3_alive (infiniflow#17294)

1. test/unit_test/api/utils/test_health_utils_s3.py: 5 cases covering
   check_s3_alive alive/timeout paths and the delegate-contract that
   mirrors check_storage's return shape (elapsed, error).
2. test/unit_test/admin/test_load_configurations.py: 10 cases covering
   the new case "s3" branch (https/http/non-default-port/omitted), a
   regression guard for the existing minio path, both-backends-in-one-
   config, and 3 malformed-endpoint "does not raise" cases.
3. test/unit_test/admin/test_get_all_services.py: 9 cases covering
   the new STORAGE_IMPL filter (minio-active, s3-active, no-config),
   the existing DOC_ENGINE retrieval filter (regression guard), and
   a parametrized env-var-to-store_type mapping test (MINIO, AWS_S3,
   OSS, GCS).
4. test/unit_test/admin/conftest.py: prepend admin/server to sys.path
   so admin modules can be imported from pytest. The admin package is
   invoked as a script in production; tests need the same path layout.
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. 🐞 bug Something isn't working, pull request that fix bug. labels Jul 27, 2026
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds S3 configuration loading and health checks to the admin service backend, filters file-store services by STORAGE_IMPL, and adds unit tests for endpoint parsing, backend selection, retrieval filtering, and health responses.

Changes

S3 admin service reporting

Layer / File(s) Summary
S3 health-check contract
api/utils/health_utils.py, test/unit_test/api/utils/test_health_utils_s3.py
Adds check_s3_alive() and tests alive, timeout, elapsed-time, and error-message behavior.
S3 configuration loading
admin/server/config.py, test/unit_test/admin/test_load_configurations.py
Loads S3 file-store configurations, derives endpoint host and port values, applies AWS defaults, preserves MinIO behavior, and handles malformed endpoints.
Active storage filtering
admin/server/services.py, test/unit_test/admin/conftest.py, test/unit_test/admin/test_get_all_services.py
Filters file-store services by normalized STORAGE_IMPL values while retaining retrieval filtering behavior and isolated test configuration state.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ConfigLoader
  participant ServiceMgr
  participant HealthCheck
  participant Storage
  ConfigLoader->>ServiceMgr: register S3 FileStoreConfig
  ServiceMgr->>ServiceMgr: filter by STORAGE_IMPL
  ServiceMgr->>HealthCheck: request S3 liveness
  HealthCheck->>Storage: check storage
  Storage-->>HealthCheck: health result
  HealthCheck-->>ServiceMgr: alive or timeout status
Loading

Possibly related PRs

Suggested labels: 🌈 python

Poem

I’m a rabbit with buckets of code,
S3 now carries the service load.
MinIO sleeps when it’s not in the plan,
Health checks report what storage can.
Hop, hop—clear status on display!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the S3/STORAGE_IMPL backend selection fix and references the linked issue.
Description check ✅ Passed The description provides a clear Summary and enough context about the bug, changes, tests, and compatibility.
Linked Issues check ✅ Passed The changes implement the issue's expected S3 config loading, STORAGE_IMPL filtering, and S3-compatible health checks.
Out of Scope Changes check ✅ Passed No clearly unrelated code changes stand out; the extra test scaffolding and malformed-endpoint handling support the stated fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@admin/server/config.py`:
- Around line 293-297: Update the S3 endpoint parsing in admin/server/config.py
around endpoint_url, parsed, and parsed.port to catch ValueError from malformed
URLs or invalid ports and fall back without aborting configuration loading. Add
coverage in test/unit_test/admin/test_load_configurations.py for non-numeric
ports, out-of-range ports, and malformed IPv6 input, verifying configuration
loading completes with the fallback behavior.

In `@api/utils/health_utils.py`:
- Around line 249-259: Update api/utils/health_utils.py lines 249-259 in
check_s3_alive to emit a debug log for the check_storage outcome without logging
raw error payloads; update admin/server/config.py lines 288-311 to log the
parsed S3 host and port while never logging endpoint_url directly; update
admin/server/services.py lines 276-293 to add debug visibility for normalized
STORAGE_IMPL filtering.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c40fac9d-e11b-439f-8a7a-318e59147d15

📥 Commits

Reviewing files that changed from the base of the PR and between 5bfd59f and 913a7e6.

📒 Files selected for processing (7)
  • admin/server/config.py
  • admin/server/services.py
  • api/utils/health_utils.py
  • test/unit_test/admin/conftest.py
  • test/unit_test/admin/test_get_all_services.py
  • test/unit_test/admin/test_load_configurations.py
  • test/unit_test/api/utils/test_health_utils_s3.py

Comment thread admin/server/config.py Outdated
Comment thread api/utils/health_utils.py
…load

`urlparse(endpoint_url)` raises ValueError on malformed IPv6 URLs,
and `parsed.port` raises ValueError on non-numeric or out-of-range
ports. A bad endpoint in the config block previously crashed the
admin status loader and stopped the UI from showing any services.

Wrapped the S3 parser in try/except ValueError and fall back to the
raw endpoint string as host with the scheme's default port so config
loading always completes. Added logging:
- `check_s3_alive` emits the check outcome at debug level
- `admin/server/config.py` logs the selected host/port after parsing
  (and the AWS S3 default when endpoint_url is unset) at debug level;
  the full endpoint_url is never logged because it may contain
  credentials in the query string.
- The malformed-URL fallback path logs at warning level.

Extended `TestLoadS3HandlesBadEndpoint` with the three concrete
malformed inputs the bug surfaced: non-numeric port, out-of-range
port, and malformed IPv6 literal.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/utils/health_utils.py`:
- Around line 258-260: Update check_s3_alive to use a defined logger before its
success and failure logging calls. Import the logging module or reuse the
module’s existing logger, ensuring both logging.debug references execute without
raising NameError and the function returns its health result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 36f3a41b-07f4-4cd2-adbe-c3ce71c4b0eb

📥 Commits

Reviewing files that changed from the base of the PR and between 913a7e6 and 4600dd8.

📒 Files selected for processing (3)
  • admin/server/config.py
  • api/utils/health_utils.py
  • test/unit_test/admin/test_load_configurations.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • admin/server/config.py
  • test/unit_test/admin/test_load_configurations.py

Comment thread api/utils/health_utils.py
Comment on lines +258 to +260
logging.debug("check_s3_alive: ok, elapsed=%s ms", payload.get("elapsed", "?"))
return {"status": "alive", "message": f"Confirm elapsed: {payload.get('elapsed', '?')} ms."}
logging.debug("check_s3_alive: failed, error=%s", payload.get("error", "unknown"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Define logging before using it.

Both paths reference an undefined name, so check_s3_alive() raises NameError instead of returning a health result. Import logging or use the module’s existing logger.

Proposed fix
+import logging
+
 def check_s3_alive():
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logging.debug("check_s3_alive: ok, elapsed=%s ms", payload.get("elapsed", "?"))
return {"status": "alive", "message": f"Confirm elapsed: {payload.get('elapsed', '?')} ms."}
logging.debug("check_s3_alive: failed, error=%s", payload.get("error", "unknown"))
import logging
logging.debug("check_s3_alive: ok, elapsed=%s ms", payload.get("elapsed", "?"))
return {"status": "alive", "message": f"Confirm elapsed: {payload.get('elapsed', '?')} ms."}
logging.debug("check_s3_alive: failed, error=%s", payload.get("error", "unknown"))
🧰 Tools
🪛 Ruff (0.15.21)

[error] 258-258: Undefined name logging

(F821)


[error] 260-260: Undefined name logging

(F821)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/utils/health_utils.py` around lines 258 - 260, Update check_s3_alive to
use a defined logger before its success and failure logging calls. Import the
logging module or reuse the module’s existing logger, ensuring both
logging.debug references execute without raising NameError and the function
returns its health result.

Source: Linters/SAST tools

@Harsh23Kashyap

Copy link
Copy Markdown
Contributor Author

Opened #17441 to honor STORAGE_IMPL when surfacing the file_store backend on the admin status page. While addressing the CodeRabbit review I also tightened the S3 endpoint parser: malformed IPv6 URLs, non-numeric ports, and out-of-range ports now fall back to a sensible default instead of aborting config load, and the S3 health/parser paths emit debug-level diagnostics (without leaking credentials). All three review threads resolved.

@JinHai-CN JinHai-CN added the ci Continue Integration label Jul 28, 2026
@JinHai-CN

Copy link
Copy Markdown
Contributor
Would reformat: admin/server/config.py
1 file would be reformatted, 6 files already formatted

exit status 1
┃  ruff ❯ 
F821 Undefined name `logging`
   --> api/utils/health_utils.py:258:9
    |
256 |     ok, payload = check_storage()
257 |     if ok:
258 |         logging.debug("check_s3_alive: ok, elapsed=%s ms", payload.get("elapsed", "?"))
    |         ^^^^^^^
259 |         return {"status": "alive", "message": f"Confirm elapsed: {payload.get('elapsed', '?')} ms."}
260 |     logging.debug("check_s3_alive: failed, error=%s", payload.get("error", "unknown"))
    |

F821 Undefined name `logging`
   --> api/utils/health_utils.py:260:5
    |
258 |         logging.debug("check_s3_alive: ok, elapsed=%s ms", payload.get("elapsed", "?"))
259 |         return {"status": "alive", "message": f"Confirm elapsed: {payload.get('elapsed', '?')} ms."}
260 |     logging.debug("check_s3_alive: failed, error=%s", payload.get("error", "unknown"))
    |     ^^^^^^^
261 |     return {"status": "timeout", "message": f"error: {payload.get('error', 'unknown')}"}
    |

Found 2 errors.

exit status 1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐞 bug Something isn't working, pull request that fix bug. ci Continue Integration size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Admin Service status still reports MinIO when object storage is configured as AWS S3

2 participants