import os import docker import logging from .version import BlenderVersion from pathlib import Path from docker.errors import BuildError, APIError, ContainerError, ImageNotFound class BlenderDocker: def __init__(self, blender: BlenderVersion, severity: int = logging.WARNING): self._blender_version = blender # Todo get a user config self._docker_client = docker.DockerClient(base_url=f'unix:///var/run/docker.sock') self.login() self._docker_image = None self._repository = 'stilobique/blender' self._docker_image_name = self._blender_version.format_to_tag(self._repository) self._docker_file = Path(os.getcwd(), 'Dockerfile') if not self._docker_file.exists(): print(f'Dockerfile not found: {self._docker_file}') exit(1) self._args = { 'b3d_vs_major': f'{self._blender_version.major}', 'b3d_vs_minor': f'{self._blender_version.minor}.{self._blender_version.hotfix}', } self._severity = severity def _get_logger(self, name: str) -> logging.Logger: """ Private Method to set a logger for a specific method set (by his name) :param name: Make the logger file name. :return: """ log_dir = Path('logs') log_dir.mkdir(parents=True, exist_ok=True) logger = logging.getLogger(f'blender_docker.{name}') logger.setLevel(self._severity) logger.propagate = False if not logger.handlers: handler = logging.FileHandler(log_dir / f'{name}.log') handler.setFormatter(logging.Formatter( '%(asctime)s|%(name)s [%(levelname)s] %(message)s' )) logger.addHandler(handler) return logger def login(self): """Login to the Docker Hub""" try: print('Login to Hub Docker.') self._docker_client.login( username=os.environ['DOCKER_HUB_USER'], password=os.environ['DOCKER_HUB_PASSWORD'] ) except APIError as e: print(f'Login failed with error: {e}') exit(1) def build(self): """Build the Blender Docker Image""" logger = self._get_logger('build') try: logger.info('Building Blender Docker Image') logger.info(f'\tBuilds withs args: {self._args}') logger.info(f'\tTag generate: {self._docker_image_name}') self._docker_image, build_logs = self._docker_client.images.build( path=os.getcwd(), # nocache=True, buildargs=self._args, tag=self._docker_image_name, quiet=False, rm=True, forcerm=True ) # Debug log: for chunk in build_logs: if 'stream' in chunk: line = chunk['stream'].strip() if line: logger.debug(line) elif 'error' in chunk: logger.error(chunk['error']) except BuildError as e: logger.exception(f'Build docker image failed: {e}') for line in e.build_log: if 'stream' in line: logger.debug(line['stream'].strip()) raise @staticmethod def tag(self): print('\tSet Tags version and/or Latest') pass def test(self): try: print('\tTesting your Blender Docker image') print(f'\tLoad: {self._docker_image_name}') test = self._docker_client.containers.run( name=f"Blender-Docker-Testing-{self._blender_version.tag_name_slugify()}", image=self._docker_image_name, command=['./blender', '--version'], auto_remove = True, remove=True, detach=True, stdout=True ) print(f'\tResult: {test.logs()}') return True except ContainerError as e: print(f'Container error: {e}') exit(1) except ImageNotFound as e: print(f'Image not found: {e}') exit(1) except APIError as e: print(f'API error: {e}') for line in e.build_log: if 'stream' in line: print(line['stream'].strip()) exit(1) def push(self): self._docker_client.images.push( repository=self._repository, tag=self._blender_version.tag_name(), stream=True, )