def test_last_line_precedence(self): base = make_tree([], [ 'garbage.md', 'thrash.md', 'README.md', 'README-bis.md', 'README-secret.md' ]) assert exclude_paths(base, ['*.md', '!README*.md', 'README-secret.md' ]) == set(['README.md', 'README-bis.md'])
def test_parent_directory(self): base = make_tree([], ['a.py', 'b.py', 'c.py']) # Dockerignore reference stipulates that absolute paths are # equivalent to relative paths, hence /../foo should be # equivalent to ../foo. It also stipulates that paths are run # through Go's filepath.Clean, which explicitely "replace # "/.." by "/" at the beginning of a path". assert exclude_paths(base, ['../a.py', '/../b.py']) == set(['c.py'])
def test_include_wildcard(self): # This may be surprising but it matches the CLI's behavior # (tested with 18.05.0-ce on linux) base = make_tree(['a'], ['a/b.py']) assert exclude_paths( base, ['*', '!*/b.py'] ) == set()
def test_last_line_precedence(self): base = make_tree( [], ['garbage.md', 'thrash.md', 'README.md', 'README-bis.md', 'README-secret.md']) assert exclude_paths( base, ['*.md', '!README*.md', 'README-secret.md'] ) == set(['README.md', 'README-bis.md'])
def test_parent_directory(self): base = make_tree( [], ['a.py', 'b.py', 'c.py']) # Dockerignore reference stipulates that absolute paths are # equivalent to relative paths, hence /../foo should be # equivalent to ../foo. It also stipulates that paths are run # through Go's filepath.Clean, which explicitely "replace # "/.." by "/" at the beginning of a path". assert exclude_paths( base, ['../a.py', '/../b.py'] ) == set(['c.py'])
def docker_context(dockerfile_content, base_path, options): context = tempfile.NamedTemporaryFile() archive = tarfile.open(mode='w', fileobj=context) archive.addfile(*prepare_string_for_tar('Dockerfile', dockerfile_content)) root = base_path # Process dockerignore dockerignore = join(root, '.dockerignore') exclude = None if exists(dockerignore): with open(dockerignore, 'r') as f: exclude = list(filter(bool, f.read().splitlines())) # Clean patterns exclude = clean_dockerignore(exclude) # Add root directory for path in sorted(exclude_paths(root, exclude)): archive.add(join(root, path), arcname=path, recursive=False) # Process options to check if we should includes a file outside the root directory for option in options.values(): if option['value']: include_file = option['def'].get('include_file', False) if include_file: archive.add(option['def']['local_path'], arcname=option['value'], recursive=False) archive.close() context.seek(0) return context
def test_no_dupes(self): paths = exclude_paths(self.base, ['!a.py']) assert sorted(paths) == sorted(set(paths))
def exclude(self, patterns, dockerfile=None): return set(exclude_paths(self.base, patterns, dockerfile=dockerfile))
def make_build_context(self): """ Makes a Docker build context from a local director. Normalises all file ownership and times so that the docker hashes align better. """ # Start temporary tar file fileobj = tempfile.NamedTemporaryFile() tfile = tarfile.open(mode='w:gz', fileobj=fileobj) # Get list of files/dirs to add to the tar paths = exclude_paths(self.container.path, []) # For each file, add it to the tar with normalisation for path in paths: disk_location = os.path.join(self.container.path, path) # For Kubernetes images, use original date values for source code user_real_time = ( 'FTL_BUILD_SRC_REAL_TIME' in os.environ and os.environ['FTL_BUILD_SRC_REAL_TIME'] == 'true' and "/src/" in disk_location ) # Directory addition if os.path.isdir(disk_location): info = tarfile.TarInfo(name=path) info.mtime = (0, os.stat(disk_location).st_mtime)[user_real_time] info.mode = 0o775 info.type = tarfile.DIRTYPE info.uid = 0 info.gid = 0 info.uname = 'root' info.gname = 'root' tfile.addfile(info) # Normal file addition elif os.path.isfile(disk_location): stat = os.stat(disk_location) info = tarfile.TarInfo(name=path) info.mtime = (0, stat.st_mtime)[user_real_time] info.size = stat.st_size info.mode = 0o775 info.type = tarfile.REGTYPE info.uid = 0 info.gid = 0 info.uname = 'root' info.gname = 'root' # Rewrite docker FROM lines with a : in them and raise a warning # TODO: Deprecate this! if path.lstrip('/') == self.container.dockerfile_name: # Read in dockerfile line by line, replacing the FROM line dockerfile = io.BytesIO() with open(disk_location, 'r') as fh: for line in fh: if line.upper().startswith('FROM') and self.container.build_parent_in_prefix: line = line.replace(':', '-') dockerfile.write(line.encode('utf8')) dockerfile.seek(0) tfile.addfile(info, dockerfile) else: with open(disk_location, 'rb') as fh: tfile.addfile(info, fh) # Ignore symlinks elif os.path.islink(disk_location): pass # Error for anything else else: raise ValueError( 'Cannot add non-file/dir {} to docker build context'.format(path) ) # Return that tarfile tfile.close() fileobj.seek(0) return fileobj
def test_include_wildcard(self): base = make_tree(['a'], ['a/b.py']) assert exclude_paths(base, ['*', '!*/b.py']) == convert_paths(['a/b.py'])
def test_exclude_include_absolute_path(self): base = make_tree([], ['a.py', 'b.py']) assert exclude_paths(base, ['/*', '!/*.py']) == set(['a.py', 'b.py'])
def test_include_wildcard(self): base = make_tree(['a'], ['a/b.py']) assert exclude_paths( base, ['*', '!*/b.py'] ) == convert_paths(['a/b.py'])
def test_exclude_include_absolute_path(self): base = make_tree([], ['a.py', 'b.py']) assert exclude_paths( base, ['/*', '!/*.py'] ) == set(['a.py', 'b.py'])
def make_build_context(self): """ Makes a Docker build context from a local directory. Normalises all file ownership and times so that the docker hashes align better. """ # Start temporary tar file fileobj = tempfile.NamedTemporaryFile() tfile = tarfile.open(mode='w:gz', fileobj=fileobj) # Get list of files/dirs to add to the tar paths = exclude_paths(self.container.path, []) # For each file, add it to the tar with normalisation for path in paths: disk_location = os.path.join(self.container.path, path) # Directory addition if os.path.isdir(disk_location): info = tarfile.TarInfo(name=path) info.mtime = 0 info.mode = 0o775 info.type = tarfile.DIRTYPE info.uid = 0 info.gid = 0 info.uname = "root" info.gname = "root" tfile.addfile(info) # Normal file addition elif os.path.isfile(disk_location): stat = os.stat(disk_location) info = tarfile.TarInfo(name=path) info.mtime = 0 info.size = stat.st_size info.mode = 0o755 info.type = tarfile.REGTYPE info.uid = 0 info.gid = 0 info.uname = "root" info.gname = "root" # Rewrite docker FROM lines with a : in them and raise a warning # TODO: Deprecate this! if path.lstrip("/") == self.container.dockerfile_name: # Read in dockerfile line by line, replacing the FROM line dockerfile = io.BytesIO() with open(disk_location, "r") as fh: for line in fh: if line.upper().startswith( "FROM " ) and self.container.build_parent_in_prefix: line = line.replace(":", "-") dockerfile.write(line.encode("utf8")) dockerfile.seek(0) tfile.addfile(info, dockerfile) else: with open(disk_location, "rb") as fh: tfile.addfile(info, fh) # Error for anything else else: raise ValueError( "Cannot add non-file/dir %s to docker build context" % path) # Return that tarfile tfile.close() fileobj.seek(0) return fileobj