summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Firshman <ben@firshman.co.uk>2016-11-29 10:09:43 +0000
committerGitHub <noreply@github.com>2016-11-29 10:09:43 +0000
commit6a16edee3e7110e0439ef067e5c11db51bd4371a (patch)
treea8a9dbbecc215bd72053cee5c4e0a0ef1b7be6d1
parent52376e4b2453576c424d57a5c662e811c8a6d577 (diff)
parent9fc8b3a730293b8cbee2feab04c355e753b20cdf (diff)
downloaddocker-py-6a16edee3e7110e0439ef067e5c11db51bd4371a.tar.gz
Merge pull request #1277 from bodnarbm/do-not-traverse-ignored-directories-with-no-potential-exceptions
Do not traverse excluded directories that are not prefixes of dockerignore exceptions.
-rw-r--r--docker/utils/utils.py51
-rw-r--r--tests/unit/utils_test.py76
2 files changed, 107 insertions, 20 deletions
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
index 823894c..b5b2f37 100644
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -214,6 +214,31 @@ def should_include(path, exclude_patterns, include_patterns):
return True
+def should_check_directory(directory_path, exclude_patterns, include_patterns):
+ """
+ Given a directory path, a list of exclude patterns, and a list of inclusion
+ patterns:
+
+ 1. Returns True if the directory path should be included according to
+ should_include.
+ 2. Returns True if the directory path is the prefix for an inclusion
+ pattern
+ 3. Returns False otherwise
+ """
+
+ # To account for exception rules, check directories if their path is a
+ # a prefix to an inclusion pattern. This logic conforms with the current
+ # docker logic (2016-10-27):
+ # https://github.com/docker/docker/blob/bc52939b0455116ab8e0da67869ec81c1a1c3e2c/pkg/archive/archive.go#L640-L671
+
+ path_with_slash = directory_path + os.sep
+ possible_child_patterns = [pattern for pattern in include_patterns if
+ (pattern + os.sep).startswith(path_with_slash)]
+ directory_included = should_include(directory_path, exclude_patterns,
+ include_patterns)
+ return directory_included or len(possible_child_patterns) > 0
+
+
def get_paths(root, exclude_patterns, include_patterns, has_exceptions=False):
paths = []
@@ -222,25 +247,13 @@ def get_paths(root, exclude_patterns, include_patterns, has_exceptions=False):
if parent == '.':
parent = ''
- # If exception rules exist, we can't skip recursing into ignored
- # directories, as we need to look for exceptions in them.
- #
- # It may be possible to optimize this further for exception patterns
- # that *couldn't* match within ignored directores.
- #
- # This matches the current docker logic (as of 2015-11-24):
- # https://github.com/docker/docker/blob/37ba67bf636b34dc5c0c0265d62a089d0492088f/pkg/archive/archive.go#L555-L557
-
- if not has_exceptions:
-
- # Remove excluded patterns from the list of directories to traverse
- # by mutating the dirs we're iterating over.
- # This looks strange, but is considered the correct way to skip
- # traversal. See https://docs.python.org/2/library/os.html#os.walk
-
- dirs[:] = [d for d in dirs if
- should_include(os.path.join(parent, d),
- exclude_patterns, include_patterns)]
+ # Remove excluded patterns from the list of directories to traverse
+ # by mutating the dirs we're iterating over.
+ # This looks strange, but is considered the correct way to skip
+ # traversal. See https://docs.python.org/2/library/os.html#os.walk
+ dirs[:] = [d for d in dirs if
+ should_check_directory(os.path.join(parent, d),
+ exclude_patterns, include_patterns)]
for path in dirs:
if should_include(os.path.join(parent, path),
diff --git a/tests/unit/utils_test.py b/tests/unit/utils_test.py
index 19d52c9..f69c62c 100644
--- a/tests/unit/utils_test.py
+++ b/tests/unit/utils_test.py
@@ -25,7 +25,9 @@ from docker.utils import (
)
from docker.utils.ports import build_port_bindings, split_port
-from docker.utils.utils import create_endpoint_config, format_environment
+from docker.utils.utils import (
+ create_endpoint_config, format_environment, should_check_directory
+)
from ..helpers import make_tree
@@ -1113,6 +1115,78 @@ class TarTest(unittest.TestCase):
)
+class ShouldCheckDirectoryTest(unittest.TestCase):
+ exclude_patterns = [
+ 'exclude_rather_large_directory',
+ 'dir/with/subdir_excluded',
+ 'dir/with/exceptions'
+ ]
+
+ include_patterns = [
+ 'dir/with/exceptions/like_this_one',
+ 'dir/with/exceptions/in/descendents'
+ ]
+
+ def test_should_check_directory_not_excluded(self):
+ self.assertTrue(
+ should_check_directory('not_excluded', self.exclude_patterns,
+ self.include_patterns)
+ )
+
+ self.assertTrue(
+ should_check_directory('dir/with', self.exclude_patterns,
+ self.include_patterns)
+ )
+
+ def test_shoud_check_parent_directories_of_excluded(self):
+ self.assertTrue(
+ should_check_directory('dir', self.exclude_patterns,
+ self.include_patterns)
+ )
+ self.assertTrue(
+ should_check_directory('dir/with', self.exclude_patterns,
+ self.include_patterns)
+ )
+
+ def test_should_not_check_excluded_directories_with_no_exceptions(self):
+ self.assertFalse(
+ should_check_directory('exclude_rather_large_directory',
+ self.exclude_patterns, self.include_patterns
+ )
+ )
+ self.assertFalse(
+ should_check_directory('dir/with/subdir_excluded',
+ self.exclude_patterns, self.include_patterns
+ )
+ )
+
+ def test_should_check_excluded_directory_with_exceptions(self):
+ self.assertTrue(
+ should_check_directory('dir/with/exceptions',
+ self.exclude_patterns, self.include_patterns
+ )
+ )
+ self.assertTrue(
+ should_check_directory('dir/with/exceptions/in',
+ self.exclude_patterns, self.include_patterns
+ )
+ )
+
+ def test_should_not_check_siblings_of_exceptions(self):
+ self.assertFalse(
+ should_check_directory('dir/with/exceptions/but_not_here',
+ self.exclude_patterns, self.include_patterns
+ )
+ )
+
+ def test_should_check_subdirectories_of_exceptions(self):
+ self.assertTrue(
+ should_check_directory('dir/with/exceptions/like_this_one/subdir',
+ self.exclude_patterns, self.include_patterns
+ )
+ )
+
+
class FormatEnvironmentTest(unittest.TestCase):
def test_format_env_binary_unicode_value(self):
env_dict = {