diff --git a/README.md b/README.md index 2ebbca6..20cc8ca 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ dockerfeed -d ../Dockerfile.special path/to/dockerimagesource | docker build -t The `-p` option will allow to add extra content to the build context. ``` -usage: dockerfeed [-h] [-p PATH] [-d DOCKERFILE] context +usage: dockerfeed [-h] [-p PATH] [-d DOCKERFILE] [-i DOCKERIGNORE] context Replace Dockerfile and/or replace file path in context @@ -31,4 +31,8 @@ optional arguments: /path/to/./path_to_be_inserted -d DOCKERFILE, --dockerfile DOCKERFILE replacement dockerfile - ``` + -i DOCKERIGNORE, --dockerignore DOCKERIGNORE + A file containing glob rules for which files to + exclude from the context. .dockerignore will be used + by default if it exists. +``` diff --git a/dockerfeed b/dockerfeed index 6589589..7d164ec 100755 --- a/dockerfeed +++ b/dockerfeed @@ -16,31 +16,65 @@ limitations under the License. """ - -from tarfile import TarFile,TarInfo +import argparse +import fnmatch +import os +import sys +from tarfile import TarFile from cStringIO import StringIO -import os, sys, argparse + + +class DockerIgnoreParser(object): + def __init__(self, ignore_string): + self.ignore_rules = [self._mangle(line) for line in ignore_string.split('\n') if line] + + def _mangle(self, line): + if line.endswith('/'): + # Everything gets passed through os.path.relpath which *strips* trailing slashes + line = line[:-1] + return line + + def ignores(self, filename): + return any(fnmatch.fnmatchcase(filename, pattern) for pattern in self.ignore_rules) + + @classmethod + def from_filename(cls, filename): + with open(filename, 'r') as f: + return cls(f.read()) + + @classmethod + def from_file_obj(cls, file_obj): + return cls(file_obj.read()) class DockerContextGenerator(object): - def __init__(self, context, dockerfile, filter_list, replacement_list): + def __init__(self, context, dockerfile, filter_list, replacement_list, dockerignore): self.context = context self.filter_list = filter_list self.replacement_list = replacement_list self.dockerfile = dockerfile + self.dockerignore = dockerignore def filter_func(self, info): - if os.path.relpath(info.name) in filter_list: + path = os.path.relpath(info.name) + in_filter_list = path in self.filter_list + + if self.dockerignore is not None: + ignored = self.dockerignore.ignores(path) + else: + ignored = False + + if in_filter_list or ignored: return None else: return info def create_context(self): stringfile = StringIO() - with TarFile.open(mode='w',fileobj=stringfile) as tarfile: + with TarFile.open(mode='w', fileobj=stringfile) as tarfile: tarfile.add(self.context, arcname='.', filter=self.filter_func) - for src,dst in self.replacement_list: - tarfile.add(os.path.join(src,dst),arcname=os.path.join('.',dst)) + for src, dst in self.replacement_list: + tarfile.add(os.path.join(src, dst), arcname=os.path.join('.', dst)) if self.dockerfile: tarfile.add(self.dockerfile, arcname='./Dockerfile') stringfile.seek(0) @@ -53,27 +87,41 @@ if __name__ == '__main__': default=[], help='path to add/override in context. example: /path/to/./path_to_be_inserted') parser.add_argument('-d', '--dockerfile', metavar='DOCKERFILE', type=str, dest='dockerfile', default=None, action='store', help='replacement dockerfile') + parser.add_argument('-i', '--dockerignore', default=None, + help='A file containing glob rules for which files to exclude from the context. ' + '.dockerignore will be used by default if it exists.') parsed = parser.parse_args() filter_list = [] replacement_list = [] dockerfile = None - if parsed.dockerfile: + if parsed.dockerfile is not None: filter_list.append('Dockerfile') dockerfile = parsed.dockerfile if not os.path.isfile(dockerfile): - parser.error('Dockerfile is expected to be a plain file') + parser.error('DOCKERFILE is expected to be a plain file') + + if parsed.dockerignore is not None: + if not os.path.isfile(parsed.dockerignore): + parser.error('DOCKERIGNORE must be a file') + dockerignore = DockerIgnoreParser.from_filename(parsed.dockerignore) + else: + if os.path.isfile('.dockerignore'): + dockerignore = DockerIgnoreParser.from_filename('.dockerignore') + else: + dockerignore = None + for path in parsed.paths: try: - (source,dest) = path.split('/./') + (source, dest) = path.split('/./') except: parser.error('wrong path syntax') filter_list.append(os.path.relpath(dest)) - replacement_list.append((source,dest)) - dcg = DockerContextGenerator(os.path.relpath(parsed.context), dockerfile, filter_list, replacement_list) + replacement_list.append((source, dest)) + dcg = DockerContextGenerator(os.path.relpath(parsed.context), + dockerfile, + filter_list, + replacement_list, + dockerignore) dcg.create_context() - - - -