def __enter__(self): from_layer = self.number_of_layers if self.number_of_layers and not self.numeric: from_layer = self.docker.history( self.image.tag)[self.number_of_layers]['Id'] squash = Squash(self.log, self.image.tag, self.docker, tag=self.tag, from_layer=from_layer, output_path=self.output_path, load_image=self.load_image, tmp_dir=self.tmp_dir, development=self.development) self.image_id = squash.run() if not self.output_path: self.history = self.docker.history(self.image_id) if self.tag: self.tar = self._save_image() with tarfile.open(fileobj=self.tar, mode='r') as tar: self.tarnames = tar.getnames() self.squashed_layer = self._squashed_layer() self.layers = [ o['Id'] for o in self.docker.history(self.image_id) ] self.metadata = self.docker.inspect_image(self.image_id) return self
def __enter__(self): from_layer = self.number_of_layers if self.number_of_layers and not self.numeric: from_layer = self.docker.history( self.image.tag)[self.number_of_layers]['Id'] squash = Squash( self.log, self.image.tag, self.docker, tag=self.tag, from_layer=from_layer, output_path=self.output_path, load_image=self.load_image, tmp_dir=self.tmp_dir, development=self.development) self.image_id = squash.run() if not self.output_path: self.history = self.docker.history(self.image_id) if self.tag: self.tar = self._save_image() with tarfile.open(fileobj=self.tar, mode='r') as tar: self.tarnames = tar.getnames() self.squashed_layer = self._squashed_layer() self.layers = [o['Id'] for o in self.docker.history(self.image_id)] self.metadata = self.docker.inspect_image(self.image_id) return self
def run(self): metadata = { "path": os.path.join(self.workflow.source.workdir, EXPORTED_SQUASHED_IMAGE_NAME) } if self.dont_load: # squash the image, don't load it back to docker Squash(log=self.log, image=self.image, from_layer=self.from_layer, tag=self.tag, output_path=metadata["path"], load_image=False).run() else: # squash the image and output both tarfile and Docker engine image new_id = Squash(log=self.log, image=self.image, from_layer=self.from_layer, tag=self.tag, output_path=metadata["path"], load_image=True).run() self.workflow.builder.image_id = new_id metadata.update(get_exported_image_metadata(metadata["path"])) self.workflow.exported_image_sequence.append(metadata) defer_removal(self.workflow, self.image)
def test_should_cleanup_after_squashing(self, v2_image): squash = Squash(self.log, 'image', self.docker_client, tag="new_image", load_image=True, cleanup=True) self.docker_client.inspect_image.return_value = {'Id': "some_id"} squash.run() calls = [mock.call(image, force=False, noprune=False) for image in ["some_id", squash.tmp_tag]] self.docker_client.remove_image.assert_has_calls(calls) self.docker_client.tag.assert_called_once_with(squash.tmp_tag, "new_image", tag=None, force=True)
def _squash(self, docker_client, image_id): LOGGER.info("Squashing image {}...".format(image_id)) squash = Squash(docker=docker_client, log=LOGGER, from_layer=self.generator.image['from'], image=image_id, cleanup=True) return squash.run()
def test_exit_if_no_output_path_provided_and_loading_is_disabled_too(self): squash = Squash(self.log, 'image', self.docker_client, load_image=False, output_path=None) squash.run() self.log.warn.assert_called_with( "No output path specified and loading into Docker is not selected either; squashed image would not accessible, proceeding with squashing doesn't make sense" )
def test_should_cleanup_after_squashing(self, v2_image): self.docker_client.inspect_image.return_value = {'Id': "abcdefgh"} squash = Squash(self.log, 'image', self.docker_client, load_image=True, cleanup=True) squash.run() self.docker_client.remove_image.assert_called_with('abcdefgh', force=False, noprune=False)
def test_should_handle_cleanup_error_when_removing_image(self, v2_image): self.docker_client.inspect_image.return_value = {'Id': "abcdefgh"} self.docker_client.remove_image.side_effect = docker.errors.APIError( "Message") squash = Squash(self.log, 'image', self.docker_client, load_image=True, cleanup=True) squash.run() self.log.info.assert_any_call("Removing old image image...") self.log.warn.assert_any_call( "Could not remove image image: Message, skipping cleanup after squashing" )
def test_should_handle_cleanup_error_while_getting_image_id( self, v2_image): self.docker_client.inspect_image.side_effect = docker.errors.APIError( "Message") squash = Squash(self.log, 'image', self.docker_client, load_image=True, cleanup=True) squash.run() self.docker_client.remove_image.assert_not_called() self.log.warn.assert_any_call( "Could not get the image ID for image image: Message, skipping cleanup after squashing" )
def run(self): if self.workflow.build_result.skip_layer_squash: return # enable build plugins to prevent unnecessary squashes if self.save_archive: output_path = os.path.join(self.workflow.source.workdir, EXPORTED_SQUASHED_IMAGE_NAME) metadata = {"path": output_path} else: output_path = None # Squash the image and output tarfile # If the parameter dont_load is set to True squashed image won't be # loaded in to Docker daemon. If it's set to False it will be loaded. new_id = Squash(log=self.log, image=self.image, from_layer=self.from_layer, tag=self.tag, output_path=output_path, load_image=not self.dont_load).run() if ':' not in new_id: # Older versions of the daemon do not include the prefix new_id = 'sha256:{}'.format(new_id) if not self.dont_load: self.workflow.builder.image_id = new_id if self.save_archive: metadata.update( get_exported_image_metadata(output_path, IMAGE_TYPE_DOCKER_ARCHIVE)) self.workflow.exported_image_sequence.append(metadata) defer_removal(self.workflow, self.image)
def run(self): if is_flatpak_build(self.workflow): # We'll extract the filesystem anyways for a Flatpak instead of exporting # the docker image directly, so squash just slows things down. self.log.info('flatpak build, skipping plugin') return # This plugin is obsoleted. This line change is just for the test pass. if getattr(self.workflow, "skip_layer_squash", False): return # enable build plugins to prevent unnecessary squashes if self.save_archive: output_path = os.path.join(self.workflow.source.workdir, EXPORTED_SQUASHED_IMAGE_NAME) metadata = {"path": output_path} else: output_path = None # Squash the image and output tarfile # If the parameter dont_load is set to True squashed image won't be # loaded in to Docker daemon. If it's set to False it will be loaded. new_id = Squash(log=self.log, image=self.image, from_layer=self.from_layer, tag=self.tag, output_path=output_path, load_image=not self.dont_load).run() if ':' not in new_id: # Older versions of the daemon do not include the prefix new_id = 'sha256:{}'.format(new_id) if not self.dont_load: self.workflow.data.image_id = new_id if self.save_archive: metadata.update( get_exported_image_metadata(output_path, IMAGE_TYPE_DOCKER_ARCHIVE)) # OSBS2 TBD exported_image_sequence will not work for multiple platform self.workflow.data.exported_image_sequence.append(metadata)
def test_should_not_cleanup_after_squashing(self, v2_image): squash = Squash(self.log, 'image', self.docker_client, load_image=True) squash.run() v2_image.cleanup.assert_not_called()
def test_handle_case_when_no_image_is_provided(self): squash = Squash(self.log, None, self.docker_client) with self.assertRaises(SquashError) as cm: squash.run() self.assertEquals(str(cm.exception), "Image is not provided")
class SquashedImage(object): def __init__(self, image, number_of_layers=None, output_path=None, load_image=True, numeric=False, tmp_dir=None, log=None, development=False, tag=True): self.image = image self.number_of_layers = number_of_layers self.docker = TestIntegSquash.docker self.log = log or TestIntegSquash.log if tag: self.tag = "%s:squashed" % self.image.name else: self.tag = None self.output_path = output_path self.load_image = load_image self.numeric = numeric self.tmp_dir = tmp_dir self.development = development def __enter__(self): from_layer = self.number_of_layers if self.number_of_layers and not self.numeric: from_layer = self.docker.history( self.image.tag)[self.number_of_layers]['Id'] self.squash = Squash( self.log, self.image.tag, self.docker, tag=self.tag, from_layer=from_layer, output_path=self.output_path, load_image=self.load_image, tmp_dir=self.tmp_dir, development=self.development) self.tmp_tag = self.squash.tmp_tag self.image_id = self.squash.run() if not self.output_path: self.history = self.docker.history(self.image_id) if self.tag: self.tar = self._save_image() with tarfile.open(fileobj=self.tar, mode='r') as tar: self.tarnames = tar.getnames() self.squashed_layer = self._squashed_layer() self.layers = [o['Id'] for o in self.docker.history(self.image_id)] self.metadata = self.docker.inspect_image(self.image_id) return self def __exit__(self, exc_type, exc_val, exc_tb): if not (os.getenv('CI') or self.output_path): self.docker.remove_image(image=self.image_id, force=True) def _save_image(self): image = self.docker.get_image(self.tmp_tag) buf = BytesIO() for chunk in image: buf.write(chunk) buf.seek(0) # Rewind return buf def _extract_file(self, name, tar_object): tar_object.seek(0) with tarfile.open(fileobj=tar_object, mode='r') as tar: member = tar.getmember(name) return tar.extractfile(member) def _squashed_layer(self): self.tar.seek(0) with tarfile.open(fileobj=self.tar, mode='r') as tar: self.squashed_layer_path = ImageHelper.top_layer_path(tar) return self._extract_file("%s/layer.tar" % self.squashed_layer_path, self.tar) def assertFileExists(self, name): self.squashed_layer.seek(0) # Rewind with tarfile.open(fileobj=self.squashed_layer, mode='r') as tar: assert name in tar.getnames( ), "File '%s' was not found in the squashed files: %s" % (name, tar.getnames()) def assertFileDoesNotExist(self, name): self.squashed_layer.seek(0) # Rewind with tarfile.open(fileobj=self.squashed_layer, mode='r') as tar: assert name not in tar.getnames( ), "File '%s' was found in the squashed layer files: %s" % (name, tar.getnames()) def assertFileIsNotHardLink(self, name): self.squashed_layer.seek(0) # Rewind with tarfile.open(fileobj=self.squashed_layer, mode='r') as tar: member = tar.getmember(name) assert member.islnk( ) == False, "File '%s' should not be a hard link, but it is" % name def assert_target_tag_exists(self): if self.tag: self.squash._switch_tmp_image_to_target_tag() self.docker.inspect_image(self.tag)#raise exception if not exists
def test_exit_if_no_output_path_provided_and_loading_is_disabled_too(self): squash = Squash(self.log, 'image', self.docker_client, load_image=False, output_path=None) squash.run() self.log.warn.assert_called_with("No output path specified and loading into Docker is not selected either; squashed image would not accessible, proceeding with squashing doesn't make sense")
def test_handle_case_when_no_image_is_provided(self): squash = Squash(self.log, None, self.docker_client) with self.assertRaises(SquashError) as cm: squash.run() self.assertEquals( str(cm.exception), "Image is not provided")