1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
"""Utility functions for working with tarballs."""
import pathlib
import random
import shutil
import tarfile
import tempfile
from fnmatch import fnmatch
from typing import BinaryIO, Optional
import sys
def prepare_containerignore(anchor: str) -> list[str]:
"""Return the list of patterns for filenames to exclude.
.containerignore takes precedence over .dockerignore.
"""
for filename in (".containerignore", ".dockerignore"):
ignore = pathlib.Path(anchor) / filename
if not ignore.exists():
continue
with ignore.open(encoding='utf-8') as file:
return list(
filter(
lambda L: L and not L.startswith("#"),
(line.strip() for line in file.readlines()),
)
)
return []
def prepare_containerfile(anchor: str, dockerfile: str) -> str:
"""Ensure that Containerfile, or a proxy Containerfile is in context_dir.
Args:
anchor: Build context directory
dockerfile: Path to Dockerfile/Containerfile
Returns:
path to Dockerfile/Containerfile in root of context directory
"""
anchor_path = pathlib.Path(anchor)
dockerfile_path = pathlib.Path(dockerfile)
if dockerfile_path.parent.samefile(anchor_path):
return dockerfile_path.name
proxy_path = anchor_path / f".containerfile.{random.getrandbits(160):x}"
shutil.copy2(dockerfile_path, proxy_path, follow_symlinks=False)
return proxy_path.name
def create_tar(
anchor: str, name: str = None, exclude: list[str] = None, gzip: bool = False
) -> BinaryIO:
"""Create a tarfile from context_dir to send to Podman service.
Args:
anchor: Directory to use as root of tar file.
name: Name of tar file.
exclude: List of patterns for files to exclude from tar file.
gzip: When True, gzip compress tar file.
"""
def add_filter(info: tarfile.TarInfo) -> Optional[tarfile.TarInfo]:
"""Filter files targeted to be added to tarfile.
Args:
info: Information on the file targeted to be added
Returns:
None: if file is not to be added
TarInfo: when file is to be added. Modified as needed.
Notes:
exclude is captured from parent
"""
if not (info.isfile() or info.isdir() or info.issym()):
return None
if _exclude_matcher(info.name, exclude):
return None
# Workaround https://bugs.python.org/issue32713. Fixed in Python 3.7
if info.mtime < 0 or info.mtime > 8**11 - 1:
info.mtime = int(info.mtime)
# do not leak client information to service
info.uid = 0
info.uname = info.gname = "root"
if sys.platform == "win32":
info.mode = info.mode & 0o755 | 0o111
return info
if name is None:
# pylint: disable=consider-using-with
name = tempfile.NamedTemporaryFile(prefix="podman_context", suffix=".tar")
else:
name = pathlib.Path(name)
if exclude is None:
exclude = []
else:
exclude = exclude.copy()
# FIXME caller needs to add this...
# exclude.append(".dockerignore")
exclude.append(name.name)
mode = "w:gz" if gzip else "w"
with tarfile.open(name.name, mode) as tar:
tar.add(anchor, arcname="", recursive=True, filter=add_filter)
return open(name.name, "rb") # pylint: disable=consider-using-with
def _exclude_matcher(path: str, exclude: list[str]) -> bool:
"""Returns True if path matches an entry in exclude.
Note:
FIXME Not compatible, support !, **, etc
"""
if not exclude:
return False
for pattern in exclude:
if fnmatch(path, pattern):
return True
return False
|