You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
6.4 KiB
190 lines
6.4 KiB
"""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'])
|
|
|