mirror of https://github.com/docker/docker-py.git
Add support for .dockerignore
Fixes #265. Implementation is a bit more elaborate than docker's implementation and matches with the one proposed in dotcloud/docker#6869 to handle permission issues more nicely.
This commit is contained in:
parent
9170219188
commit
87b4d327d1
|
@ -13,6 +13,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import struct
|
||||
|
@ -351,7 +352,12 @@ class Client(requests.Session):
|
|||
'git://', 'github.com/')):
|
||||
remote = path
|
||||
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:
|
||||
stream = True
|
||||
|
|
|
@ -13,9 +13,11 @@
|
|||
# limitations under the License.
|
||||
|
||||
import io
|
||||
import os
|
||||
import tarfile
|
||||
import tempfile
|
||||
from distutils.version import StrictVersion
|
||||
from fnmatch import fnmatch
|
||||
|
||||
import requests
|
||||
import six
|
||||
|
@ -42,10 +44,29 @@ def mkbuildcontext(dockerfile):
|
|||
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()
|
||||
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()
|
||||
f.seek(0)
|
||||
return f
|
||||
|
|
|
@ -17,6 +17,7 @@ import base64
|
|||
import json
|
||||
import io
|
||||
import os
|
||||
import shutil
|
||||
import signal
|
||||
import tempfile
|
||||
import unittest
|
||||
|
@ -24,6 +25,8 @@ import unittest
|
|||
import docker
|
||||
import six
|
||||
|
||||
from tests.test import Cleanup
|
||||
|
||||
# FIXME: missing tests for
|
||||
# export; history; import_image; insert; port; push; tag; get; load
|
||||
|
||||
|
@ -820,6 +823,43 @@ class TestBuildWithAuth(BaseTestCase):
|
|||
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 #
|
||||
#######################
|
||||
|
|
|
@ -17,7 +17,10 @@ import datetime
|
|||
import io
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import signal
|
||||
import sys
|
||||
import tarfile
|
||||
import tempfile
|
||||
import unittest
|
||||
import gzip
|
||||
|
@ -58,9 +61,34 @@ url_prefix = 'http+unix://var/run/docker.sock/v{0}/'.format(
|
|||
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,
|
||||
put=fake_request, delete=fake_request)
|
||||
class DockerClientTest(unittest.TestCase):
|
||||
class DockerClientTest(Cleanup, unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.client = docker.Client()
|
||||
# Force-clear authconfig to avoid tampering with the tests
|
||||
|
@ -1350,11 +1378,13 @@ class DockerClientTest(unittest.TestCase):
|
|||
|
||||
def test_load_config_no_file(self):
|
||||
folder = tempfile.mkdtemp()
|
||||
self.addCleanup(shutil.rmtree, folder)
|
||||
cfg = docker.auth.load_config(folder)
|
||||
self.assertTrue(cfg is not None)
|
||||
|
||||
def test_load_config(self):
|
||||
folder = tempfile.mkdtemp()
|
||||
self.addCleanup(shutil.rmtree, folder)
|
||||
f = open(os.path.join(folder, '.dockercfg'), 'w')
|
||||
auth_ = base64.b64encode(b'sakuya:izayoi').decode('ascii')
|
||||
f.write('auth = {0}\n'.format(auth_))
|
||||
|
@ -1369,6 +1399,26 @@ class DockerClientTest(unittest.TestCase):
|
|||
self.assertEqual(cfg['email'], 'sakuya@scarlet.net')
|
||||
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__':
|
||||
unittest.main()
|
||||
|
|
Loading…
Reference in New Issue