Skip to content

[draft] S3 results upload - #1032

Draft
jj10306 wants to merge 5 commits into
mainfrom
s3-results-upload
Draft

jj10306 wants to merge 5 commits into
mainfrom
s3-results-upload

Conversation

@jj10306

@jj10306 jj10306 commented Sep 14, 2026

Copy link
Copy Markdown

No description provided.

Introduce cloudai.util.object_store, a domain-free transport layer for
publishing artifacts to object storage:

- ObjectStore ABC with upload_file/uri plus a shared upload_directory()
  that walks a tree, preserves relative paths, honours exclude globs, and
  collects per-file failures into UploadStats instead of aborting.
- S3ObjectStore, backed by boto3. Credentials come from boto3's standard
  chain (AWS_* env, ~/.aws/credentials, IAM role), so no secrets are ever
  read from CloudAI configuration.

boto3 is an optional 'cloudai[s3]' extra reached through the existing
LazyImports pattern, matching how gymnasium is handled. This keeps it out
of module-level imports, which the ruff banned-module-level-imports rule
and the filterwarnings=["error"] pytest setting both require.

The module lives under cloudai.util so the import-linter leaf-dependency
contract keeps it free of cloudai domain concepts.
ResultsUploadConfig subclasses ReportConfig with the destination fields
(bucket, prefix, endpoint_url, region) plus upload_tree/upload_tarball/
exclude toggles. Each destination field falls back to a CLOUDAI_S3_*
environment variable via default_factory, so TOML wins when set and the
environment supplies the default otherwise. ReportConfig forbids extra
keys, so every field has to be declared explicitly.

ResultsUploadReporter uploads self.results_root, which the Reporter base
class already hands it. Two deliberate choices:

- It does not call load_test_runs(). This reporter treats the directory as
  raw bytes, so coupling the upload to workload parsing would only add
  failure modes.
- upload_tarball() creates the tarball when absent. TarballReporter only
  writes <results_root>.tgz when a test run failed, so its presence cannot
  be assumed; the archiving logic is reused rather than duplicated.

A missing bucket warns loudly instead of silently succeeding, so a
misconfigured upload is not mistaken for an empty one.
Registry.ordered_scenario_reports() sorts by a hardcoded map in which any
unlisted name falls through to priority 1. Left at that default the
uploader would run before StatusReporter, DSEReporter and TarballReporter,
and would therefore publish an incomplete results directory. Give
"results_upload" priority 5 so it always observes the finished tree.

Registered with enable=False: shipping results off-box has to be opt-in.
Registering here also surfaces it in `cloudai list reports` for free.
tests/util/test_object_store.py drives upload_directory() through a small
in-memory ObjectStore rather than mocking the ABC, so the shared walk
logic - relative-path preservation, exclude globs, and failure collection
- is tested independently of boto3. The S3 tests patch
cloudai.util.object_store.lazy, matching the repo convention of patching
at the consumer's import site.

tests/test_reporter.py covers the reporter: env-var fallback and TOML
precedence, the missing-bucket and missing-directory guards, and the
tarball being created when absent but reused when present.

Two existing tests needed updating for the new registration:
- test_report_order asserted positions by negative index.
- test_init hardcodes the registry contents and asserted that every report
  except junit defaults to enabled; results_upload joins junit as opt-in.
Covers the cloudai[s3] extra, every config option with its environment
variable fallback, the fact that credentials come from boto3's standard
chain rather than CloudAI config, that an upload failure does not change
the run's exit status, and how to upload an earlier run's directory with
generate-report.
@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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

@abstractmethod
def uri(self, key: str) -> str:
"""Return a human-readable URI for the given key, for logging."""
...
@abstractmethod
def upload_file(self, local_path: Path, key: str) -> None:
"""Upload a single file to the given key."""
...
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.

1 participant