import tarfile import requests import os from tempfile import TemporaryFile, mkdtemp from zipfile import ZipFile from util.dockerfileparse import parse_dockerfile from util.safetar import safe_extractall class BuildPackageException(Exception): """ Exception raised when retrieving or parsing a build package. """ pass class BuildPackage(object): """ Helper class for easy reading and updating of a Dockerfile build pack. """ def __init__(self, requests_file): self._mime_processors = { 'application/zip': BuildPackage._prepare_zip, 'application/x-zip-compressed': BuildPackage._prepare_zip, 'text/plain': BuildPackage._prepare_dockerfile, 'application/octet-stream': BuildPackage._prepare_dockerfile, 'application/x-tar': BuildPackage._prepare_tarball, 'application/gzip': BuildPackage._prepare_tarball, 'application/x-gzip': BuildPackage._prepare_tarball, } c_type = requests_file.headers['content-type'] c_type = c_type.split(';')[0] if ';' in c_type else c_type if c_type not in self._mime_processors: raise BuildPackageException('Unknown build package mime type: %s' % c_type) self._package_directory = None try: self._package_directory = self._mime_processors[c_type](requests_file) except Exception as ex: raise BuildPackageException(ex.message) def parse_dockerfile(self, subdirectory): dockerfile_path = os.path.join(self._package_directory, subdirectory, 'Dockerfile') if not os.path.exists(dockerfile_path): if subdirectory: message = 'Build package did not contain a Dockerfile at sub directory %s.' % subdirectory else: message = 'Build package did not contain a Dockerfile at the root directory.' raise BuildPackageException(message) with open(dockerfile_path, 'r') as dockerfileobj: return parse_dockerfile(dockerfileobj.read()) @staticmethod def from_url(url): buildpack_resource = requests.get(url, stream=True) return BuildPackage(buildpack_resource) @staticmethod def _prepare_zip(request_file): build_dir = mkdtemp(prefix='docker-build-') # Save the zip file to temp somewhere with TemporaryFile() as zip_file: zip_file.write(request_file.content) to_extract = ZipFile(zip_file) to_extract.extractall(build_dir) return build_dir @staticmethod def _prepare_dockerfile(request_file): build_dir = mkdtemp(prefix='docker-build-') dockerfile_path = os.path.join(build_dir, "Dockerfile") with open(dockerfile_path, 'w') as dockerfile: dockerfile.write(request_file.content) return build_dir @staticmethod def _prepare_tarball(request_file): build_dir = mkdtemp(prefix='docker-build-') # Save the zip file to temp somewhere with tarfile.open(mode='r|*', fileobj=request_file.raw) as tar_stream: safe_extractall(tar_stream, build_dir) return build_dir