This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new bdcb7eddd85 Skip Kubernetes volume internals when discovering Dag and 
plugin files (#76186)
bdcb7eddd85 is described below

commit bdcb7eddd8505a33c2b513cf208fdd2f0a06fdd2
Author: Snehashis Lenka <[email protected]>
AuthorDate: Sun Nov 1 02:13:42 2026 -0800

    Skip Kubernetes volume internals when discovering Dag and plugin files 
(#76186)
    
    ConfigMap, Secret and projected volumes keep their files in a hidden
    "..<timestamp>" directory linked as "..data", and expose each entry as
    a symlink through it. Walking both reached that directory twice, so the
    recursive-loop check failed discovery for any such mounted folder.
    
    Co-authored-by: Claude Opus 5.5 <[email protected]>
    Co-authored-by: Jarek Potiuk <[email protected]>
---
 .../src/airflow_shared/module_loading/file_discovery.py |  3 +++
 .../tests/module_loading/test_file_discovery.py         | 17 +++++++++++++++++
 2 files changed, 20 insertions(+)

diff --git 
a/shared/module_loading/src/airflow_shared/module_loading/file_discovery.py 
b/shared/module_loading/src/airflow_shared/module_loading/file_discovery.py
index 00660816b25..4837a6bbb9b 100644
--- a/shared/module_loading/src/airflow_shared/module_loading/file_discovery.py
+++ b/shared/module_loading/src/airflow_shared/module_loading/file_discovery.py
@@ -174,6 +174,9 @@ def _find_path_from_directory(
                 # evaluation order of patterns is important with negation
                 # so that later patterns can override earlier patterns
 
+        # Kubernetes volume internals; the visible entries are symlinks 
through "..data" to the same files
+        if "..data" in dirs:
+            dirs[:] = [subdir for subdir in dirs if not 
subdir.startswith("..")]
         dirs[:] = [subdir for subdir in dirs if not 
ignore_rule_type.match(Path(root) / subdir, patterns)]
         # explicit loop for infinite recursion detection since we are 
following symlinks in this walk
         for sd in dirs:
diff --git a/shared/module_loading/tests/module_loading/test_file_discovery.py 
b/shared/module_loading/tests/module_loading/test_file_discovery.py
index 388ad79c239..71dcfe5b818 100644
--- a/shared/module_loading/tests/module_loading/test_file_discovery.py
+++ b/shared/module_loading/tests/module_loading/test_file_discovery.py
@@ -73,6 +73,23 @@ class TestFindPathFromDirectory:
         with pytest.raises(RuntimeError, match=error_message):
             list(find_path_from_directory(test_dir, ignore_list_file, 
ignore_file_syntax="glob"))
 
+    @pytest.mark.parametrize("ignore_file_syntax", ["glob", "regexp"])
+    def test_find_path_from_directory_skips_kubernetes_volume_internals(self, 
tmp_path, ignore_file_syntax):
+        """Files of a Kubernetes ConfigMap or Secret volume are found once, 
via their visible symlinks."""
+        # the kubelet keeps the files in a timestamped directory, linked as 
"..data"
+        timestamped_dir = tmp_path / "..2026_10_31_12_00_00.123456789"
+        (timestamped_dir / "subdir").mkdir(parents=True)
+        (timestamped_dir / ".airflowignore").write_text("ignored.py")
+        for name in ("plugin.py", "ignored.py", "subdir/nested.py"):
+            (timestamped_dir / name).touch()
+        (tmp_path / "..data").symlink_to(timestamped_dir.name)
+        for name in (".airflowignore", "plugin.py", "ignored.py", "subdir"):
+            (tmp_path / name).symlink_to(Path("..data", name))
+
+        found = sorted(find_path_from_directory(tmp_path, ".airflowignore", 
ignore_file_syntax))
+
+        assert found == [str(tmp_path / "plugin.py"), str(tmp_path / "subdir" 
/ "nested.py")]
+
     def 
test_find_path_from_directory_keeps_inherited_rules_on_symlink_switch(self, 
tmp_path):
         """Ignore rules inherited by a subdirectory still apply if the Dag 
folder symlink is switched mid-walk."""
         for revision in ("rev1", "rev2"):

Reply via email to