1
0
mirror of https://github.com/projectatomic/atomic.git synced 2026-02-07 06:44:52 +01:00
Files
atomic/Atomic/verify.py
Giuseppe Scrivano a242c42871 validate: add option --all
it will validate all the images in a storage.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>

Closes: #1150
Approved by: rhatdan
2018-01-05 09:35:43 +00:00

177 lines
7.8 KiB
Python

from . import util
from . import Atomic
from operator import itemgetter
import argparse
from .discovery import RegistryInspect
from .client import no_shaw
from Atomic.backendutils import BackendUtils
ATOMIC_CONFIG = util.get_atomic_config()
storage = ATOMIC_CONFIG.get('default_storage', "docker")
def cli(subparser, hidden=False):
# atomic verify
if hidden:
verifyp = subparser.add_parser("verify", argument_default=argparse.SUPPRESS)
else:
verifyp = subparser.add_parser(
"verify", help=_("verify image is fully updated"),
epilog="atomic verify checks whether there is a newer image "
"available and scans through all layers to see if any of "
"the sublayers have a new version available")
verifyp.set_defaults(_class=Verify, func='verify')
verifyp.add_argument("image", nargs='?', help=_("container image"))
verifyp.add_argument("-a", "--all", default=False, dest="all",
action="store_true",
help=_("verify all images in a storage"))
verifyp.add_argument("--no-validate", default=False, dest="no_validate",
action="store_true",
help=_("disable validating system images"))
verifyp.add_argument("--storage", default=None, dest="storage",
help=_("Specify the storage of the image. "
"If not specified and there are images with the same name in "
"different storages, you will be prompted to specify."))
verifyp.add_argument("-v", "--verbose", default=False,
action="store_true",
help=_("Report status of each layer"))
class Verify(Atomic):
def __init__(self):
super(Verify, self).__init__()
self.debug = False
self.backend_utils = BackendUtils()
def _layers_match(self, local, remote):
_match = []
for _layer_int in range(len(local)):
if local[_layer_int] == remote[_layer_int]:
_match.append(True)
else:
_match.append(False)
return all(_match)
def verify(self):
if self.args.image and self.args.all:
raise ValueError("Incompatible options specified. --all doesn't support an image name")
if not self.args.all and not self.args.image:
raise ValueError("Please specify the image name")
if self.args.all and not self.args.storage:
raise ValueError("Please specify --storage")
if self.args.all:
be = BackendUtils().get_backend_from_string(self.args.storage)
images = be.get_images()
for i in images:
if i.repotags is None:
continue
img_name = i.repotags[0]
d = util.Decompose(img_name)
if d.registry == "":
util.write_err("Image {} not fully qualified: skipping".format(img_name))
continue
self._verify_one_image(img_name)
else:
return self._verify_one_image(self.args.image)
def _verify_one_image(self, image):
if self.args.debug:
util.write_out(str(self.args))
be, local_layers, remote_layers = self._verify(image)
if not self._layers_match(local_layers, remote_layers) or self.args.verbose:
col = "{0:30} {1:20} {2:20} {3:1}"
util.write_out("\n{} contains the following images:\n".format(image))
util.write_out(col.format("NAME", "LOCAL VERSION", "REMOTE VERSION", "DIFFERS"))
for layer_int in range(len(local_layers)):
differs = 'NO' if remote_layers[layer_int] == local_layers[layer_int] else 'YES'
util.write_out(col.format(local_layers[layer_int].name[:30],
local_layers[layer_int].long_version[:20],
remote_layers[layer_int].long_version[:20],
differs))
util.write_out("\n")
if not self.args.no_validate:
be.validate_layer(image)
def verify_dbus(self):
_, local_layers, remote_layers = self._verify(self.args.image)
layers = []
for layer_int in range(len(local_layers)):
layer = {}
layer['name'] = local_layers[layer_int].name
layer['local_version'] = local_layers[layer_int].long_version
layer['remote_version'] = remote_layers[layer_int].long_version
layer['differs'] = False if remote_layers[layer_int] == local_layers[layer_int] else True
layers.append(layer)
return layers
def _verify(self, image):
be, img_obj = self.backend_utils.get_backend_and_image_obj(image, str_preferred_backend=self.args.storage or storage, required=True if self.args.storage else False)
remote_img_name = "{}:latest".format(util.Decompose(img_obj.fq_name).no_tag)
remote_img_obj = be.make_remote_image(remote_img_name)
return be, img_obj.layers, remote_img_obj.layers
def get_tagged_images(self, names, layers):
"""
Returns a dict with image names and its tag name.
:param names:
:param layers:
:return: list of sorted dicts (by index)
"""
base_images = []
for name in names:
_match = next((x for x in layers if x['Name'] == name and x['RepoTags'] is not ''), None)
registry, repo, image, tag, digest = util.Decompose(self.get_fq_image_name(_match['RepoTags'][0])).all
tag = "latest"
ri = RegistryInspect(registry=registry, repo=repo, image=image, tag=tag, digest=digest, debug=self.debug)
remote_inspect = ri.inspect()
release = remote_inspect.get("Labels", None).get("Release", None)
version = remote_inspect.get("Labels", None).get("Version", None)
if release and version:
remote_version = "{}-{}-{}".format(name, version, release)
else:
# Check if the blob exists on the registry by the ID
remote_id = no_shaw(ri.remote_id)
_match['Version'] = _match['Id']
remote_version = remote_id if remote_id is not None else ""
_match['Remote Version'] = remote_version
base_images.append(_match)
return sorted(base_images, key=itemgetter('index'))
@staticmethod
def _mismatch(layer):
if layer['Version'] != layer['Remote Version'] and layer['Remote Version'] != layer['Id']:
return "Yes"
if layer['Version'] == '' and layer['Remote Version'] == '':
return "!"
return "No"
@staticmethod
def print_verify(base_images, image, verbose=False):
"""
Implements a verbose printout of layers. Can be called with
atomic verify -v or if we detect some layer does not have
versioning information.
:param base_images:
:param image:
:return: None
"""
def check_for_updates(base_images):
for i in base_images:
if Verify._mismatch(i) in ['Yes', '!']:
return True
return False
has_updates = check_for_updates(base_images)
if has_updates or verbose:
col = "{0:30} {1:20} {2:20} {3:1}"
util.write_out("\n{} contains the following images:\n".format(image))
util.write_out(col.format("NAME", "LOCAL VERSION", "REMOTE VERSION", "DIFFERS"))
for _image in base_images:
util.write_out(col.format(_image['Name'][:30], _image['Version'][:20], _image['Remote Version'][:20], Verify._mismatch(_image)))
util.write_out("\n")