"""Models for manipulating images in/to/from storage.""" from __future__ import absolute_import import collections import copy import json import logging from . import ConfigDict, flatten, fold_keys from .containers import Container class Image(collections.UserDict): """Model for an Image.""" def __init__(self, client, id, data): """Construct Image Model.""" # pylint: disable=redefined-builtin super().__init__(data) for k, v in data.items(): setattr(self, k, v) self._id = id self._client = client assert self._id == data['id'],\ 'Requested image id({}) does not match store id({})'.format( self._id, data['id'] ) @staticmethod def _split_token(values=None, sep='='): if not values: return {} return {k: v1 for k, v1 in (v0.split(sep, 1) for v0 in values)} def create(self, *_args, **kwargs): """Create container from image. Pulls defaults from image.inspect() """ details = self.inspect() config = ConfigDict(image_id=self._id, **kwargs) config['command'] = details.config.get('cmd') config['env'] = self._split_token(details.config.get('env')) config['image'] = copy.deepcopy(details.repotags[0]) config['labels'] = copy.deepcopy(details.labels) # TODO: Are these settings still required? config['net_mode'] = 'bridge' config['network'] = 'bridge' config['args'] = flatten([config['image'], config['command']]) logging.debug('Image %s: create config: %s', self._id, config) with self._client() as podman: id_ = podman.CreateContainer(config)['container'] cntr = podman.GetContainer(id_) return Container(self._client, id_, cntr['container']) container = create def export(self, dest, compressed=False): """Write image to dest, return id on success.""" with self._client() as podman: results = podman.ExportImage(self._id, dest, compressed) return results['image'] def history(self): """Retrieve image history.""" with self._client() as podman: for r in podman.HistoryImage(self._id)['history']: yield collections.namedtuple('HistoryDetail', r.keys())(**r) def inspect(self): """Retrieve details about image.""" with self._client() as podman: results = podman.InspectImage(self._id) obj = json.loads(results['image'], object_hook=fold_keys()) return collections.namedtuple('ImageInspect', obj.keys())(**obj) def push(self, target, compress=False, manifest_format="", remove_signatures=False, sign_by=""): """Copy image to target, return id on success.""" with self._client() as podman: results = podman.PushImage(self._id, target, compress, manifest_format, remove_signatures, sign_by) return results['reply']['id'] def remove(self, force=False): """Delete image, return id on success. force=True, stop any running containers using image. """ with self._client() as podman: results = podman.RemoveImage(self._id, force) return results['image'] def tag(self, tag): """Tag image.""" with self._client() as podman: results = podman.TagImage(self._id, tag) return results['image'] class Images(): """Model for Images collection.""" def __init__(self, client): """Construct model for Images collection.""" self._client = client def list(self): """List all images in the libpod image store.""" with self._client() as podman: results = podman.ListImages() for img in results['images']: yield Image(self._client, img['id'], img) def build(self, dockerfile=None, tags=None, **kwargs): """Build container from image. See podman-build.1.md for kwargs details. """ if dockerfile is None: raise ValueError('"dockerfile" is a required argument.') if not hasattr(dockerfile, '__iter__'): raise ValueError('"dockerfile" is required to be an iter.') if tags is None: raise ValueError('"tags" is a required argument.') if not hasattr(tags, '__iter__'): raise ValueError('"tags" is required to be an iter.') config = ConfigDict(dockerfile=dockerfile, tags=tags, **kwargs) with self._client() as podman: result = podman.BuildImage(config) return self.get(result['image']['id']), \ (line for line in result['image']['logs']) def delete_unused(self): """Delete Images not associated with a container.""" with self._client() as podman: results = podman.DeleteUnusedImages() return results['images'] def import_image(self, source, reference, message='', changes=None): """Read image tarball from source and save in image store.""" with self._client() as podman: results = podman.ImportImage(source, reference, message, changes) return results['image'] def pull(self, source): """Copy image from registry to image store.""" with self._client() as podman: results = podman.PullImage(source) return results['reply']['id'] def search(self, id_, limit=25, is_official=None, is_automated=None, star_count=None): """Search registries for id.""" constraints = {} if is_official is not None: constraints['is_official'] = is_official if is_automated is not None: constraints['is_automated'] = is_automated if star_count is not None: constraints['star_count'] = star_count with self._client() as podman: results = podman.SearchImages(id_, limit, constraints) for img in results['results']: yield collections.namedtuple('ImageSearch', img.keys())(**img) def get(self, id_): """Get Image from id.""" with self._client() as podman: result = podman.GetImage(id_) return Image(self._client, result['image']['id'], result['image'])