Skip to content

Commit f3920b7

Browse files
committed
docker_workspace: support dockerignore to filter files from the workspace
1 parent 8a7a95a commit f3920b7

File tree

2 files changed

+67
-5
lines changed

2 files changed

+67
-5
lines changed

torchx/workspace/docker_workspace.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
# This source code is licensed under the BSD-style license found in the
55
# LICENSE file in the root directory of this source tree.
66

7+
import fnmatch
78
import io
89
import logging
910
import posixpath
1011
import tarfile
1112
import tempfile
12-
from typing import IO, TYPE_CHECKING, Optional, Dict, Tuple, Mapping
13+
from typing import IO, TYPE_CHECKING, Optional, Dict, Tuple, Mapping, Iterable
1314

1415
import fsspec
1516
import torchx
@@ -129,17 +130,41 @@ def _build_context(img: str, workspace: str) -> IO[bytes]:
129130
return f
130131

131132

132-
def _copy_to_tarfile(workspace: str, tf: tarfile.TarFile) -> None:
133-
# TODO(d4l3k) implement docker ignore files
133+
def _glob_any(s: str, patterns: Iterable[str]) -> bool:
134+
for pattern in patterns:
135+
if fnmatch.fnmatch(s, pattern):
136+
return True
137+
return False
138+
134139

140+
def _copy_to_tarfile(workspace: str, tf: tarfile.TarFile) -> None:
135141
fs, path = fsspec.core.url_to_fs(workspace)
136142
assert isinstance(path, str), "path must be str"
137143

144+
# load dockerignore
145+
# https://docs.docker.com/engine/reference/builder/#dockerignore-file
146+
ignore_patterns = set()
147+
ignore_path = posixpath.join(path, ".dockerignore")
148+
if fs.exists(ignore_path):
149+
with fs.open(ignore_path, "rt") as f:
150+
lines = f.readlines()
151+
for line in lines:
152+
line, _, _ = line.partition("#")
153+
line = line.strip()
154+
if len(line) == 0:
155+
continue
156+
ignore_patterns.add(line)
157+
138158
for dir, dirs, files in fs.walk(path, detail=True):
139159
assert isinstance(dir, str), "path must be str"
140160
relpath = posixpath.relpath(dir, path)
161+
if _glob_any(relpath, ignore_patterns):
162+
continue
141163
for file, info in files.items():
142164
with fs.open(info["name"], "rb") as f:
143-
tinfo = tarfile.TarInfo(posixpath.join(relpath, file))
165+
filepath = posixpath.join(relpath, file) if relpath != "." else file
166+
if _glob_any(filepath, ignore_patterns):
167+
continue
168+
tinfo = tarfile.TarInfo(filepath)
144169
tinfo.size = info["size"]
145170
tf.addfile(tinfo, f)

torchx/workspace/test/docker_workspace_test.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@
44
# This source code is licensed under the BSD-style license found in the
55
# LICENSE file in the root directory of this source tree.
66

7+
import tarfile
78
import unittest
89
from unittest.mock import MagicMock
910

1011
import fsspec
1112
from torchx.specs import Role, AppDef
12-
from torchx.workspace.docker_workspace import DockerWorkspace
13+
from torchx.workspace.docker_workspace import (
14+
DockerWorkspace,
15+
_build_context,
16+
)
1317

1418

1519
def has_docker() -> bool:
@@ -114,3 +118,36 @@ def test_push_images(self) -> None:
114118
def test_push_images_empty(self) -> None:
115119
workspace = DockerWorkspace()
116120
workspace._push_images({})
121+
122+
def test_dockerignore(self) -> None:
123+
fs = fsspec.filesystem("memory")
124+
fs.touch("dockerignore/ignoredir/bar")
125+
fs.touch("dockerignore/dir1/bar")
126+
fs.touch("dockerignore/dir/ignorefileglob")
127+
fs.touch("dockerignore/dir/ignorefile")
128+
fs.touch("dockerignore/ignorefile")
129+
fs.touch("dockerignore/dir/file")
130+
fs.touch("dockerignore/foo.sh")
131+
with fs.open("dockerignore/.dockerignore", "wt") as f:
132+
f.write(
133+
"""
134+
# comment
135+
ignoredir
136+
ignorefile
137+
*/ignorefileglo*
138+
dir?
139+
"""
140+
)
141+
142+
with _build_context("img", "memory://dockerignore") as f:
143+
with tarfile.open(fileobj=f, mode="r") as tf:
144+
self.assertCountEqual(
145+
tf.getnames(),
146+
{
147+
"Dockerfile",
148+
"foo.sh",
149+
".dockerignore",
150+
"dir/ignorefile",
151+
"dir/file",
152+
},
153+
)

0 commit comments

Comments
 (0)