def build_external_dockerfile(client, image): import docker.errors cprint(" Building base image from %s" % image, 'blue') assert not image.built stream = client.api.build(path=os.path.dirname(image.path), dockerfile=os.path.basename(image.path), tag=image.tag, decode=True, rm=True) try: utils.stream_docker_logs(stream, image) except (ValueError, docker.errors.APIError) as e: raise errors.ExternalBuildError( 'Error building Dockerfile at %s. ' % image.path + 'Please check it for errors\n. Docker API error message:' + str(e)) image.built = True cprint(" Finished building Dockerfile at %s" % image.path, 'green')
def build(self, client, pull=False, usecache=True): """ Drives an individual build step. Build steps are separated by build_directory. If a build has zero one or less build_directories, it will be built in a single step. Args: client (docker.Client): docker client object that will build the image pull (bool): whether to pull dependent layers from remote repositories usecache (bool): whether to use cached layers or rebuild from scratch """ print(colored(' Building step', 'blue'), colored(self.imagename, 'blue', attrs=['bold']), colored('defined in', 'blue'), colored(self.sourcefile, 'blue', attrs=['bold'])) if self.build_first and not self.build_first.built: self.build_external_dockerfile(client, self.build_first) if self.bust_cache: usecache = False if not usecache: cprint(' Build cache disabled - this image will be rebuilt from scratch', 'yellow') dockerfile = u'\n'.join(self.dockerfile_lines) kwargs = dict(tag=self.buildname, pull=pull, nocache=not usecache, decode=True, rm=True, buildargs=self.buildargs) if usecache: utils.set_build_cachefrom(self.cache_from, kwargs, client) if self.build_dir is not None: tempdir = self.write_dockerfile(dockerfile) context_path = os.path.abspath(os.path.expanduser(self.build_dir)) kwargs.update(fileobj=None, dockerfile=os.path.join(DOCKER_TMPDIR, 'Dockerfile')) print(colored(' Build context:', 'blue'), colored(os.path.relpath(context_path), 'blue', attrs=['bold'])) if not self.custom_exclude: kwargs.update(path=context_path) else: print(colored(' Custom .dockerignore from:','blue'), colored(os.path.relpath(self.ignoredefs_file), 'blue', attrs=['bold'])) context = docker.utils.tar(self.build_dir, exclude=self.custom_exclude, dockerfile=os.path.join(DOCKER_TMPDIR, 'Dockerfile'), gzip=False) kwargs.update(fileobj=context, custom_context=True) else: if sys.version_info.major == 2: kwargs.update(fileobj=StringIO(dockerfile), path=None, dockerfile=None) else: kwargs.update(fileobj=BytesIO(dockerfile.encode('utf-8')), path=None, dockerfile=None) tempdir = None # start the build stream = client.api.build(**kwargs) try: utils.stream_docker_logs(stream, self.buildname) except (ValueError, docker.errors.APIError) as e: raise errors.BuildError(dockerfile, str(e), kwargs) # remove the temporary dockerfile if tempdir is not None: os.unlink(os.path.join(tempdir, 'Dockerfile')) os.rmdir(tempdir)
def build(self, client, pull=False, usecache=True): """ Drives an individual build step. Build steps are separated by build_directory. If a build has zero one or less build_directories, it will be built in a single step. Args: client (docker.Client): docker client object that will build the image pull (bool): whether to pull dependent layers from remote repositories usecache (bool): whether to use cached layers or rebuild from scratch """ print( colored(" Building step", "blue"), colored(self.imagename, "blue", attrs=["bold"]), colored("defined in", "blue"), colored(self.sourcefile, "blue", attrs=["bold"]), ) if self.build_first and not self.build_first.built: self.build_external_dockerfile(client, self.build_first) if self.bust_cache: usecache = False if not usecache: cprint( " Build cache disabled - this image will be rebuilt from scratch", "yellow", ) dockerfile = "\n".join(self.dockerfile_lines) kwargs = dict( tag=self.buildname, pull=pull, nocache=not usecache, decode=True, rm=True, buildargs=self.buildargs, squash=self.squash, ) if usecache: utils.set_build_cachefrom(self.cache_from, kwargs, client) if self.build_dir is not None: tempdir = self.write_dockerfile(dockerfile) context_path = os.path.abspath(os.path.expanduser(self.build_dir)) kwargs.update(fileobj=None, dockerfile=os.path.join(DOCKER_TMPDIR, "Dockerfile")) print( colored(" Build context:", "blue"), colored(os.path.relpath(context_path), "blue", attrs=["bold"]), ) if not self.custom_exclude: kwargs.update(path=context_path) else: print( colored(" Custom .dockerignore from:", "blue"), colored(os.path.relpath(self.ignoredefs_file), "blue", attrs=["bold"]), ) # AMV - this is a brittle call to an apparently "private' docker sdk method context = docker.utils.tar( self.build_dir, exclude=self.custom_exclude, dockerfile=(os.path.join(DOCKER_TMPDIR, "Dockerfile"), dockerfile), gzip=False, ) kwargs.update(fileobj=context, custom_context=True) else: if sys.version_info.major == 2: fileobj = StringIO(dockerfile) else: fileobj = BytesIO(dockerfile.encode("utf-8")) kwargs.update(fileobj=fileobj, path=None, dockerfile=None) tempdir = None # start the build stream = client.api.build(**kwargs) try: utils.stream_docker_logs(stream, self.buildname) except docker.errors.APIError as e: if self.squash and not client.version().get("Experimental", False): raise errors.ExperimentalDaemonRequiredError( "Docker error message:\n " + str(e) + "\n\nUsing `squash` and/or `secret_files` requires a docker" " daemon with experimental features enabled. See\n" " https://github.com/docker/docker-ce/blob/master/components/cli/" "experimental/README.md") else: raise errors.BuildError(dockerfile, str(e), kwargs) except ValueError as e: raise errors.BuildError(dockerfile, str(e), kwargs) if self.squash and not self.bust_cache: self._resolve_squash_cache(client) # remove the temporary dockerfile if tempdir is not None: os.unlink(os.path.join(tempdir, "Dockerfile")) os.rmdir(tempdir)