Repository navigation
Keep S3 Dag bundle downloads within the configured directory - #73756
Conversation
|
Congratulations on your first Pull Request and welcome to the Apache Airflow community! If you have any issues or are unsure about any anything please check our Contributors' Guide
|
rjgoyln
left a comment
There was a problem hiding this comment.
LGTM. Just one small nit on the test.
One non-blocking note: GCSHook.sync_to_local_dir has the same prefix/relative_to pattern, but that can be addressed separately.
Just my thoughts, feel free to resolve it.
Drafted by Claude Code (Opus 5); reviewed by @rjgoyln before posting.
d0484a7 to
df16438
Compare
vincbeck
left a comment
There was a problem hiding this comment.
I understand the confusion but S3 prefix are not folders/directories.
For example, syncing dags also lists dags_archive/old.py, which cannot be made relative to the dags directory
This is valid to me, if you specify dags as prefix then dags_archive/old.py should be listed
df16438 to
5207787
Compare
|
Following up after moving the prefix normalization into S3DagBundle. The targeted tests and local checks passed. Could a maintainer approve the pending CI runs and take another look when available? Thanks! |
S3 lists keys using string prefixes, so a directory prefix without a trailing slash also selects sibling files and directories. Those keys cannot be mapped below the requested local directory and prevent a Dag bundle from refreshing.
The exact-prefix case fails differently from sibling keys, so its name should make that boundary clear in CI failures.
The bundle validates a directory prefix during initialization, but downloads with a raw string prefix. Matching objects outside that directory can prevent initialization and refresh.
5207787 to
9a354fa
Compare
potiuk
left a comment
There was a problem hiding this comment.
Approving. I checked the containment logic against .., absolute keys, keys that share the start of the prefix or equal it, trailing/double slashes and symlinks, and none escapes the bundle directory. One small pre-existing gap for a follow-up: a key like dags/. doesn't end in /, so it isn't skipped, and pathlib reduces it to the bundle directory itself. The download then fails on every refresh, which lets anyone who can write under the prefix block bundle updates. Rejecting an empty or . relative path before downloading would close it.
Drafted-by: Claude Code (Opus 5.5); reviewed by @potiuk before posting
S3DagBundledocuments its prefix as a subdirectory and checks that directory during initialization, but passes the original string prefix to the download hook. Withprefix="dags", an object such asdags_archive/old.pymatches the S3 listing and then fails local path conversion. An object named exactlydagsalso makes initialization fail when downloaded to the bundle directory itself.Append
/to non-empty directory prefixes inS3DagBundle.refresh()before calling the hook. This aligns downloads with the bundle's directory validation. The configured prefix, local file layout, URLs, and locking remain unchanged. Empty prefixes still download the whole bucket, and prefixes already ending in/are unchanged.The revised patch leaves
S3Hook.sync_to_local_dir()unchanged: generic S3 prefixes retain their string-prefix meaning. The regression now exercises bundle initialization and subsequent refresh through the real hook and boto3 with Moto, including stale local file cleanup and preservation of the excluded remote objects.For configurations without a trailing slash, the listing request changes from, for example,
dagstodags/. IAM policies using an exacts3:prefixcondition must allow that directory prefix. This behavior change is documented in the bundle guide and pending changelog note.Validation:
ValueError, twoIsADirectoryError) and all pass with the fix. The four already-delimited cases pass on both versions.No live AWS or IAM policy evaluation was performed. The broader dependent-provider, older-Airflow, scheduler/worker integration, and OS/Python matrices were not run locally.
Was generative AI tooling used to co-author this PR?
Generated-by: OpenAI Codex (GPT-6) following the guidelines