Kaydet (Commit) 8e452649 authored tarafından Joffrey F's avatar Joffrey F

Merge pull request #268 from brutasse/265-dockerignore

Add support for .dockerignore
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
# limitations under the License. # limitations under the License.
import json import json
import os
import re import re
import shlex import shlex
import struct import struct
...@@ -356,7 +357,12 @@ class Client(requests.Session): ...@@ -356,7 +357,12 @@ class Client(requests.Session):
'git://', 'github.com/')): 'git://', 'github.com/')):
remote = path remote = path
else: else:
context = utils.tar(path) dockerignore = os.path.join(path, '.dockerignore')
exclude = None
if os.path.exists(dockerignore):
with open(dockerignore, 'r') as f:
exclude = list(filter(bool, f.read().split('\n')))
context = utils.tar(path, exclude=exclude)
if utils.compare_version('1.8', self._version) >= 0: if utils.compare_version('1.8', self._version) >= 0:
stream = True stream = True
......
...@@ -13,9 +13,11 @@ ...@@ -13,9 +13,11 @@
# limitations under the License. # limitations under the License.
import io import io
import os
import tarfile import tarfile
import tempfile import tempfile
from distutils.version import StrictVersion from distutils.version import StrictVersion
from fnmatch import fnmatch
import requests import requests
import six import six
...@@ -47,10 +49,29 @@ def mkbuildcontext(dockerfile): ...@@ -47,10 +49,29 @@ def mkbuildcontext(dockerfile):
return f return f
def tar(path): def fnmatch_any(relpath, patterns):
return any([fnmatch(relpath, pattern) for pattern in patterns])
def tar(path, exclude=None):
f = tempfile.NamedTemporaryFile() f = tempfile.NamedTemporaryFile()
t = tarfile.open(mode='w', fileobj=f) t = tarfile.open(mode='w', fileobj=f)
t.add(path, arcname='.') for dirpath, dirnames, filenames in os.walk(path):
relpath = os.path.relpath(dirpath, path)
if relpath == '.':
relpath = ''
if exclude is None:
fnames = filenames
else:
dirnames[:] = [d for d in dirnames
if not fnmatch_any(os.path.join(relpath, d),
exclude)]
fnames = [name for name in filenames
if not fnmatch_any(os.path.join(relpath, name),
exclude)]
for name in fnames:
arcname = os.path.join(relpath, name)
t.add(os.path.join(path, arcname), arcname=arcname)
t.close() t.close()
f.seek(0) f.seek(0)
return f return f
......
...@@ -25,6 +25,8 @@ import unittest ...@@ -25,6 +25,8 @@ import unittest
import docker import docker
import six import six
from tests.test import Cleanup
# FIXME: missing tests for # FIXME: missing tests for
# export; history; import_image; insert; port; push; tag; get; load # export; history; import_image; insert; port; push; tag; get; load
...@@ -828,6 +830,43 @@ class TestBuildWithAuth(BaseTestCase): ...@@ -828,6 +830,43 @@ class TestBuildWithAuth(BaseTestCase):
self.assertEqual(logs.find('HTTP code: 403'), -1) self.assertEqual(logs.find('HTTP code: 403'), -1)
class TestBuildWithDockerignore(Cleanup, BaseTestCase):
def runTest(self):
if self.client._version < 1.8:
return
base_dir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, base_dir)
with open(os.path.join(base_dir, 'Dockerfile'), 'w') as f:
f.write("\n".join([
'FROM busybox',
'MAINTAINER docker-py',
'ADD . /test',
'RUN ls -A /test',
]))
with open(os.path.join(base_dir, '.dockerignore'), 'w') as f:
f.write("\n".join([
'node_modules',
'', # empty line
]))
with open(os.path.join(base_dir, 'not-ignored'), 'w') as f:
f.write("this file should not be ignored")
subdir = os.path.join(base_dir, 'node_modules', 'grunt-cli')
os.makedirs(subdir)
with open(os.path.join(subdir, 'grunt'), 'w') as f:
f.write("grunt")
stream = self.client.build(path=base_dir, stream=True)
logs = ''
for chunk in stream:
logs += chunk
self.assertFalse('node_modules' in logs)
self.assertTrue('not-ignored' in logs)
####################### #######################
# PY SPECIFIC TESTS # # PY SPECIFIC TESTS #
####################### #######################
......
...@@ -17,7 +17,10 @@ import datetime ...@@ -17,7 +17,10 @@ import datetime
import io import io
import json import json
import os import os
import shutil
import signal import signal
import sys
import tarfile
import tempfile import tempfile
import unittest import unittest
import gzip import gzip
...@@ -58,9 +61,34 @@ url_prefix = 'http+unix://var/run/docker.sock/v{0}/'.format( ...@@ -58,9 +61,34 @@ url_prefix = 'http+unix://var/run/docker.sock/v{0}/'.format(
docker.client.DEFAULT_DOCKER_API_VERSION) docker.client.DEFAULT_DOCKER_API_VERSION)
class Cleanup(object):
if sys.version_info < (2, 7):
# Provide a basic implementation of addCleanup for Python < 2.7
def __init__(self, *args, **kwargs):
super(Cleanup, self).__init__(*args, **kwargs)
self._cleanups = []
def tearDown(self):
super(Cleanup, self).tearDown()
ok = True
while self._cleanups:
fn, args, kwargs = self._cleanups.pop(-1)
try:
fn(*args, **kwargs)
except KeyboardInterrupt:
raise
except:
ok = False
if not ok:
raise
def addCleanup(self, function, *args, **kwargs):
self._cleanups.append((function, args, kwargs))
@mock.patch.multiple('docker.Client', get=fake_request, post=fake_request, @mock.patch.multiple('docker.Client', get=fake_request, post=fake_request,
put=fake_request, delete=fake_request) put=fake_request, delete=fake_request)
class DockerClientTest(unittest.TestCase): class DockerClientTest(Cleanup, unittest.TestCase):
def setUp(self): def setUp(self):
self.client = docker.Client() self.client = docker.Client()
# Force-clear authconfig to avoid tampering with the tests # Force-clear authconfig to avoid tampering with the tests
...@@ -1350,11 +1378,13 @@ class DockerClientTest(unittest.TestCase): ...@@ -1350,11 +1378,13 @@ class DockerClientTest(unittest.TestCase):
def test_load_config_no_file(self): def test_load_config_no_file(self):
folder = tempfile.mkdtemp() folder = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, folder)
cfg = docker.auth.load_config(folder) cfg = docker.auth.load_config(folder)
self.assertTrue(cfg is not None) self.assertTrue(cfg is not None)
def test_load_config(self): def test_load_config(self):
folder = tempfile.mkdtemp() folder = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, folder)
f = open(os.path.join(folder, '.dockercfg'), 'w') f = open(os.path.join(folder, '.dockercfg'), 'w')
auth_ = base64.b64encode(b'sakuya:izayoi').decode('ascii') auth_ = base64.b64encode(b'sakuya:izayoi').decode('ascii')
f.write('auth = {0}\n'.format(auth_)) f.write('auth = {0}\n'.format(auth_))
...@@ -1369,6 +1399,26 @@ class DockerClientTest(unittest.TestCase): ...@@ -1369,6 +1399,26 @@ class DockerClientTest(unittest.TestCase):
self.assertEqual(cfg['email'], 'sakuya@scarlet.net') self.assertEqual(cfg['email'], 'sakuya@scarlet.net')
self.assertEqual(cfg.get('auth'), None) self.assertEqual(cfg.get('auth'), None)
def test_tar_with_excludes(self):
base = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, base)
for d in ['test/foo', 'bar']:
os.makedirs(os.path.join(base, d))
for f in ['a.txt', 'b.py', 'other.png']:
with open(os.path.join(base, d, f), 'w') as f:
f.write("content")
for exclude, names in (
(['*.py'], ['bar/a.txt', 'bar/other.png',
'test/foo/a.txt', 'test/foo/other.png']),
(['*.png', 'bar'], ['test/foo/a.txt', 'test/foo/b.py']),
(['test/foo', 'a.txt'], ['bar/a.txt', 'bar/b.py',
'bar/other.png']),
):
archive = docker.utils.tar(base, exclude=exclude)
tar = tarfile.open(fileobj=archive)
self.assertEqual(sorted(tar.getnames()), names)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment