Beispiel #1
0
    def delete(self):
        if self.args.debug:
            util.write_out(str(self.args))

        if len(self.args.containers) > 0 and self.args.all:
            raise ValueError("You must select --all or provide a list of containers to delete.")

        beu = BackendUtils()
        if self.args.all:
            container_objects = beu.get_containers()
        else:
            container_objects = []
            for con in self.args.containers:
                _, con_obj = beu.get_backend_and_container_obj(con, str_preferred_backend=storage)
                container_objects.append(con_obj)

        four_col = "   {0:12} {1:20} {2:25} {3:10}"
        util.write_out(four_col.format("ID", "NAME", 'IMAGE_NAME', "STORAGE"))
        for con in container_objects:
            util.write_out(four_col.format(con.id[0:12], con.name[0:20], con.image_name[0:25], con.backend.backend))
        if not util.confirm_input("\nDo you wish to delete the following containers?\n"):
            util.write_out("Aborting...")
            return

        for del_con in container_objects:
            try:
                del_con.backend.delete_container(del_con.id, force=self.args.force)
            except APIError as e:
                util.write_err("Failed to delete container {}: {}".format(con.id, e))
Beispiel #2
0
 def __init__(self, policy_filename=None):
     """
     :param policy_filename: override policy filename
     """
     super(Pull, self).__init__()
     self.policy_filename=policy_filename
     self.be_utils = BackendUtils()
Beispiel #3
0
    def pull_image(self):
        storage_set = False if self.args.storage is None else True
        storage = _storage if not storage_set else self.args.storage
        check_storage_is_available(storage)
        if self.args.debug:
            write_out(str(self.args))

        src_creds = getattr(self.args, 'src_creds', None)
        if src_creds == "":
            src_creds = None

        be_utils = BackendUtils()
        be = be_utils.get_backend_from_string(storage)
        self.args.policy_filename = self.policy_filename
        try:
            if be.backend == 'docker':
                remote_image_obj = be.make_remote_image(self.args.image)
                if remote_image_obj.is_system_type and not storage_set:
                    be = be_utils.get_backend_from_string('ostree')
                    be_utils.message_backend_change('docker', 'ostree')
            elif be.backend == "containers-storage":
                remote_image_obj = be.make_remote_image(self.args.image)
            else:
                remote_image_obj = None
            be.pull_image(self.args.image, remote_image_obj, debug=self.args.debug, assumeyes=self.args.assumeyes, src_creds=src_creds)
        except ValueError as e:
            raise ValueError("Failed: {}".format(e))
        return 0
Beispiel #4
0
    def tag_image(self):
        """
        Tag an image with a different name
        :return: 0 if the tag was created
        """
        if self.args.debug:
            util.write_out(str(self.args))

        beu = BackendUtils()

        backend = None
        if self.args.storage:
            backend = beu.get_backend_from_string(self.args.storage)
            image = backend.has_image(self.args.src)

        else:
            backend, image = beu.get_backend_and_image_obj(self.args.src,
                                                           required=False)

        if not backend or not image:
            raise ValueError("Cannot find image {}.".format(self.args.src))

        backend.tag_image(self.args.src, self.args.target)

        # We need to return something here for dbus
        return 0
Beispiel #5
0
    def tag_image(self):
        """
        Tag an image with a different name
        :return: 0 if the tag was created
        """
        if self.args.debug:
            util.write_out(str(self.args))

        beu = BackendUtils()

        backend = None
        if self.args.storage:
            backend = beu.get_backend_from_string(self.args.storage)
            image  = backend.has_image(self.args.src)

        else:            
            backend, image = beu.get_backend_and_image_obj(self.args.src, required=False)

        if not backend or not image:
            raise ValueError("Cannot find image {}.".format(self.args.src))

        backend.tag_image(self.args.src, self.args.target)

        # We need to return something here for dbus
        return 0
Beispiel #6
0
    def update(self):
        if self.args.debug:
            write_out(str(self.args))

        if self.args.all and self.args.image is not None:
            raise ValueError("Cannot specify both --all and an image name")

        if self.args.all and self.args.force:
            raise ValueError("Cannot specify both --all and --force")

        if self.args.all and self.args.storage is None:
            raise ValueError("Please specify --storage")

        beu = BackendUtils()

        if self.args.all:
            be = beu.get_backend_from_string(self.args.storage)
            return self.update_all_images(be, self.args.debug)

        try:
            be, img_obj = beu.get_backend_and_image_obj(
                self.image,
                str_preferred_backend=self.args.storage or storage,
                required=True if self.args.storage else False)
            input_name = img_obj.input_name
        except ValueError:
            raise ValueError("{} not found locally.  Unable to update".format(
                self.image))

        be.update(input_name,
                  debug=self.args.debug,
                  force=self.args.force,
                  image_object=img_obj)
        return 0
Beispiel #7
0
    def delete_image(self):
        """
        Mark given image(s) for deletion from registry
        :return: 0 if all images marked for deletion, otherwise 2 on any failure
        """
        if self.args.debug:
            util.write_out(str(self.args))

        if len(self.args.delete_targets) > 0 and self.args.all:
            raise ValueError("You must select --all or provide a list of images to delete.")

        beu = BackendUtils()
        delete_objects = []

        # We need to decide on new returns for dbus because we now check image
        # validity prior to executing the delete.  If there is going to be a
        # failure, it will be here.
        #
        # The failure here is basically that it couldnt verify/find the image.

        if self.args.all:
            delete_objects = beu.get_images(get_all=True)
        else:
            for image in self.args.delete_targets:
                _, img_obj = beu.get_backend_and_image_obj(image, str_preferred_backend=self.args.storage)
                delete_objects.append(img_obj)

        if self.args.remote:
            return self._delete_remote(self.args.delete_targets)

        _image_names = []
        for del_obj in delete_objects:
            if del_obj.repotags:
                _image_names.append(len(del_obj.repotags[0]))
            else:
                _image_names.append(len(del_obj.id))
        max_img_name = max(_image_names) + 2

        if not self.args.assumeyes:
            util.write_out("Do you wish to delete the following images?\n")
            two_col = "   {0:" + str(max_img_name) + "} {1}"
            util.write_out(two_col.format("IMAGE", "STORAGE"))
            for del_obj in delete_objects:
                image = None if not del_obj.repotags else del_obj.repotags[0]
                if image is None or "<none>" in image:
                    image = del_obj.id[0:12]
                util.write_out(two_col.format(image, del_obj.backend.backend))
            confirm = util.input("\nConfirm (y/N) ")
            confirm = confirm.strip().lower()
            if not confirm in ['y', 'yes']:
                util.write_err("User aborted delete operation for {}".format(self.args.delete_targets))
                sys.exit(2)

        # Perform the delete
        for del_obj in delete_objects:
            del_obj.backend.delete_image(del_obj.input_name, force=self.args.force)

        # We need to return something here for dbus
        return
Beispiel #8
0
    def stop(self):

        if self.args.debug:
            util.write_out(str(self.args))

        beu = BackendUtils()
        be, con_obj = beu.get_backend_and_container_obj(self.args.container, storage)
        be.stop_container(con_obj, atomic=self, args=self.args)
        return 0
Beispiel #9
0
 def update(self):
     if self.args.debug:
         write_out(str(self.args))
     beu = BackendUtils()
     try:
         be, img_obj = beu.get_backend_and_image_obj(self.image, str_preferred_backend=self.args.storage or storage, required=True if self.args.storage else False)
         input_name = img_obj.input_name
     except ValueError:
         raise ValueError("{} not found locally.  Unable to update".format(self.image))
     be.update(input_name, self.args)
Beispiel #10
0
 def update(self):
     if self.args.debug:
         write_out(str(self.args))
     beu = BackendUtils()
     try:
         be, img_obj = beu.get_backend_and_image_obj(self.image, str_preferred_backend=self.args.storage or storage, required=True if self.args.storage else False)
         input_name = img_obj.input_name
     except ValueError:
         raise ValueError("{} not found locally.  Unable to update".format(self.image))
     be.update(input_name, debug=self.args.debug, force=self.args.force, image_object=img_obj)
     return 0
Beispiel #11
0
    def pull_image(self):
        if self.args.debug:
            write_out(str(self.args))

        be_utils = BackendUtils()
        be = be_utils.get_backend_from_string(self.args.storage)
        self.args.policy_filename = self.policy_filename
        try:
            be.pull_image(self.args.image, debug=self.args.debug)
        except ValueError as e:
            write_out(str(e))
            return 0
Beispiel #12
0
 def _get_scan_list(self):
     beu = BackendUtils()
     if self.args.images:
         scan_list = beu.get_images()
     elif self.args.containers:
         scan_list = beu.get_containers()
     elif self.args.all:
         scan_list = beu.get_images() + beu.get_containers()
     else:
         scan_list = []
         for scan_target in self.args.scan_targets:
             try:
                 # get_backend_and_container throws a ValueError when it cannot find anything
                 _, scan_obj = beu.get_backend_and_container_obj(
                     scan_target)
             except ValueError:
                 try:
                     # get_backend_and_image throws a ValueError when it cannot find anything
                     _, scan_obj = beu.get_backend_and_image_obj(
                         scan_target)
                 except ValueError:
                     raise ValueError(
                         "Unable to locate the container or image '{}'".
                         format(scan_target))
             scan_list.append(scan_obj)
     return scan_list
Beispiel #13
0
 def _ps(self):
     def _check_filters():
         if not self.args.filters:
             return True
         for f in self.args.filters:
             _filter, _ = f.split('=', 1)
             if _filter not in [x for x in self.FILTER_KEYWORDS]:
                 raise ValueError("The filter {} is not valid.  "
                                  "Please choose from {}".format(_filter, [x for x in self.FILTER_KEYWORDS]))
     _check_filters()
     beu = BackendUtils()
     containers = self.filter_container_objects(beu.get_containers())
     self._mark_vulnerable(containers)
     if self.args.all:
         return containers
     return [x for x in containers if x.running]
Beispiel #14
0
    def uninstall(self):
        if self.args.debug:
            util.write_out(str(self.args))

        beu = BackendUtils()
        try:
            be, img_obj = beu.get_backend_and_image_obj(self.args.image, str_preferred_backend=self.args.storage)
        except ValueError as e:
            if 'ostree' in [x().backend for x in beu.available_backends]:
                ost = OSTreeBackend()
                img_obj = ost.has_container(self.args.image)
                if not img_obj:
                    raise ValueError(e)
                be = ost
        be.uninstall(img_obj, name=self.args.name, atomic=self, ignore=self.args.ignore)
        return 0
Beispiel #15
0
    def delete_image(self):
        """
        Mark given image(s) for deletion from registry
        :return: 0 if all images marked for deletion, otherwise 2 on any failure
        """
        if self.args.debug:
            util.write_out(str(self.args))

        beu = BackendUtils()
        # Ensure the input values match up first
        delete_objects = []

        # We need to decide on new returns for dbus because we now check image
        # validity prior to executing the delete.  If there is going to be a
        # failure, it will be here.
        #
        # The failure here is basically that it couldnt verify/find the image.

        for image in self.args.delete_targets:
            be, img_obj = beu.get_backend_and_image(image, str_preferred_backend=self.args.storage)
            delete_objects.append((be, img_obj))

        if self.args.remote:
            return self._delete_remote(self.args.delete_targets)

        max_img_name = max([len(x.input_name) for _, x in delete_objects]) + 2

        if not self.args.assumeyes:
            util.write_out("Do you wish to delete the following images?\n")
            two_col = "   {0:" + str(max_img_name) + "} {1}"
            util.write_out(two_col.format("IMAGE", "STORAGE"))
            for del_obj in delete_objects:
                be, img_obj = del_obj
                util.write_out(two_col.format(img_obj.input_name, be.backend))
            confirm = util.input("\nConfirm (y/N) ")
            confirm = confirm.strip().lower()
            if not confirm in ['y', 'yes']:
                util.write_err("User aborted delete operation for {}".format(self.args.delete_targets))
                sys.exit(2)

        # Perform the delete
        for del_obj in delete_objects:
            be, img_obj = del_obj
            be.delete_image(img_obj.input_name, force=self.args.force)

        # We need to return something here for dbus
        return
Beispiel #16
0
    def uninstall(self):
        if self.args.debug:
            util.write_out(str(self.args))

        beu = BackendUtils()
        try:
            be, img_obj = beu.get_backend_and_image_obj(
                self.args.image, str_preferred_backend=self.args.storage)
        except ValueError as e:
            if 'ostree' in [x().backend for x in beu.available_backends]:
                ost = OSTreeBackend()
                img_obj = ost.has_container(self.args.image)
                if not img_obj:
                    raise ValueError(e)
                be = ost
        be.uninstall(img_obj, name=self.args.name, atomic=self)
        return 0
Beispiel #17
0
    def stop(self):

        if self.args.debug:
            util.write_out(str(self.args))

        beu = BackendUtils()
        be, con_obj = beu.get_backend_and_container_obj(self.args.container, storage)
        con_obj.stop_args = con_obj.get_label('stop')
        if con_obj.stop_args and be.backend == 'docker':
            cmd = self.gen_cmd(con_obj.stop_args + self.quote(self.args.args))
            cmd = self.sub_env_strings(cmd)
            self.display(cmd)
            # There should be some error handling around this
            # in case it fails.  And what should then be done?
            util.check_call(cmd, env=self.cmd_env())

        be.stop_container(con_obj)
Beispiel #18
0
 def run(self):
     storage_set = False if self.args.storage is None else True
     storage = _storage if not storage_set else self.args.storage
     be_utils = BackendUtils()
     if self.name:
         try:
             be, con_obj = be_utils.get_backend_and_container_obj(self.name)
             return be.run(con_obj, atomic=self, args=self.args)
         except ValueError:
             pass
     be = be_utils.get_backend_from_string(storage)
     db = DockerBackend()
     img_object = be.has_image(self.image)
     if img_object is None and storage == 'docker':
         self.display("Need to pull %s" % self.image)
         remote_image_obj = db.make_remote_image(self.args.image)
         # If the image has a atomic.type of system, then we need to land
         # this in the ostree backend.  Install it and then start it
         # because this is run
         if remote_image_obj.is_system_type and not storage_set:
             be = be_utils.get_backend_from_string('ostree')
             be_utils.message_backend_change('docker', 'ostree')
             be.install(self.image, self.name)
             con_obj = be.has_container(self.name)
             return be.run(con_obj)
         if self.args.display:
             return 0
         try:
             db.pull_image(self.image, remote_image_obj)
             img_object = db.has_image(self.image)
         except RegistryInspectError:
             raise ValueError("Unable to find image {}".format(self.image))
     return be.run(img_object, atomic=self, args=self.args)
Beispiel #19
0
    def stop(self):

        if self.args.debug:
            util.write_out(str(self.args))

        beu = BackendUtils()
        be, con_obj = beu.get_backend_and_container_obj(
            self.args.container, storage)
        con_obj.stop_args = con_obj.get_label('stop')
        if con_obj.stop_args and be.backend == 'docker':
            cmd = self.gen_cmd(con_obj.stop_args + self.quote(self.args.args))
            cmd = self.sub_env_strings(cmd)
            self.display(cmd)
            # There should be some error handling around this
            # in case it fails.  And what should then be done?
            util.check_call(cmd, env=self.cmd_env())

        be.stop_container(con_obj)
Beispiel #20
0
class TestBackendUtils(unittest.TestCase):

    bu = BackendUtils()

    def test_get_backend_for_image_preferred(self):
        pass

    def test_get_backend_for_image(self):
        pass
Beispiel #21
0
    def pull_image(self):
        storage_set = False if self.args.storage is None else True
        storage = _storage if not storage_set else self.args.storage
        check_storage_is_available(storage)
        if self.args.debug:
            write_out(str(self.args))

        be_utils = BackendUtils()
        be = be_utils.get_backend_from_string(storage)
        self.args.policy_filename = self.policy_filename
        try:
            if be.backend == 'docker':
                remote_image_obj = be.make_remote_image(self.args.image)
                if remote_image_obj.is_system_type and not storage_set:
                    be = be_utils.get_backend_from_string('ostree')
                    be_utils.message_backend_change('docker', 'ostree')
            else:
                remote_image_obj = None
            be.pull_image(self.args.image,
                          remote_image_obj,
                          debug=self.args.debug,
                          assumeyes=self.args.assumeyes)
        except ValueError as e:
            raise ValueError("Failed: {}".format(e))
        return 0
Beispiel #22
0
    def run(self):
        if self.name:
            be_utils = BackendUtils()
            try:
                be, con_obj = be_utils.get_backend_and_container_obj(self.name)
                return be.run(con_obj, atomic=self, args=self.args)
            except ValueError:
                pass

        db = DockerBackend()
        img_object = db.has_image(self.image)
        if img_object is None:
            self.display("Need to pull %s" % self.image)
            if self.args.display:
                return 0
            try:
                db.pull_image(self.image)
                img_object = db.has_image(self.image)
            except RegistryInspectError:
                raise ValueError("Unable to find image {}".format(self.image))

        db.run(img_object, atomic=self, args=self.args)
Beispiel #23
0
    def install(self):
        debug = self.args.debug
        if self.args.debug:
            util.write_out(str(self.args))
        be_utils = BackendUtils()
        try:
            # Check to see if the container already exists
            _, _ = be_utils.get_backend_and_container_obj(self.name)
            raise ValueError("A container '%s' is already present" % self.name)
        except ValueError:
            pass

        if self.user:
            if not util.is_user_mode():
                raise ValueError("--user does not work for privileged user")
            return self.syscontainers.install_user_container(self.image, self.name)
        elif self.system:
            return self.syscontainers.install(self.image, self.name)
        elif OSTREE_PRESENT and self.args.setvalues:
            raise ValueError("--set is valid only when used with --system or --user")

        # Assumed backend now is docker
        be = be_utils.get_backend_from_string('docker')

        # If the image is already present,
        img_obj = be.has_image(self.image)
        if img_obj is None:
            be.pull_image(self.image, debug=debug)
            img_obj = be.has_image(self.image)
        install_args = img_obj.get_label('INSTALL')
        if not install_args:
            return 0
        install_args = install_args.split()
        cmd = self.sub_env_strings(self.gen_cmd(install_args + self.quote(self.args.args)))
        self.display(cmd)

        if not self.args.display:
            return util.check_call(cmd)
Beispiel #24
0
    def install(self):
        if self.args.debug:
            util.write_out(str(self.args))
        storage_set = False if self.args.storage is None else True
        storage = _storage if not storage_set else self.args.storage
        args_system = getattr(self.args, 'system', None)
        args_user= getattr(self.args, 'user', None)
        if (args_system or args_user) and storage != 'ostree' and storage_set:
            raise ValueError("The --system and --user options are only available for the 'ostree' storage.")
        be_utils = BackendUtils()
        try:
            # Check to see if the container already exists
            _, _ = be_utils.get_backend_and_container_obj(self.name)
            raise ValueError("A container '%s' is already present" % self.name)
        except ValueError:
            pass

        if self.user:
            if not util.is_user_mode():
                raise ValueError("--user does not work for privileged user")
            return self.syscontainers.install_user_container(self.image, self.name)
        elif self.system or storage == 'ostree':
            return self.syscontainers.install(self.image, self.name)
        elif OSTREE_PRESENT and self.args.setvalues:
            raise ValueError("--set is valid only when used with --system or --user")

        # Assumed backend now is docker
        be = be_utils.get_backend_from_string('docker')

        # If the image is already present,
        img_obj = be.has_image(self.image)
        if img_obj is None:
            remote_image_obj = be.make_remote_image(self.args.image)
            # We found an atomic.type of system, therefore install it onto the ostree
            # backend
            if remote_image_obj.is_system_type and not storage_set:
                be_utils.message_backend_change('docker', 'ostree')
                return self.syscontainers.install(self.image, self.name)
            be.pull_image(self.args.image, remote_image_obj, debug=self.args.debug)
            img_obj = be.has_image(self.image)
        install_args = img_obj.get_label('INSTALL')
        if not install_args:
            return 0
        install_args = install_args.split()
        cmd = self.sub_env_strings(self.gen_cmd(install_args + self.quote(self.args.args)))
        self.display(cmd)

        if not self.args.display:
            try:
                name = img_obj.fq_name
            except RegistryInspectError:
                name = img_obj.input_name
            install_data = {}
            install_data[name] = {'id': img_obj.id,
                                  'install_date': strftime("%Y-%m-%d %H:%M:%S", gmtime())
                                  }
            util.InstallData.write_install_data(install_data)
            return util.check_call(cmd)
Beispiel #25
0
    def run(self):
        if self.name:
            be_utils = BackendUtils()
            try:
                be, con_obj = be_utils.get_backend_and_container_obj(self.name)
                return be.run(con_obj, atomic=self, args=self.args)
            except ValueError:
                pass


        db = DockerBackend()
        img_object = db.has_image(self.image)
        if img_object is None:
            self.display("Need to pull %s" % self.image)
            if self.args.display:
                return 0
            try:
                db.pull_image(self.image)
                img_object = db.has_image(self.image)
            except RegistryInspectError:
                util.write_err("Unable to find image {}".format(self.image))

        db.run(img_object, atomic=self, args=self.args)
Beispiel #26
0
    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)
Beispiel #27
0
    def prune_images(self):
        """
        Remove dangling images from registry
        :return: 0 if all images deleted or no dangling images found
        """

        if self.args.debug:
            util.write_out(str(self.args))

        beu = BackendUtils()
        for backend in beu.available_backends:
            be = backend()
            be.prune()

        return 0
Beispiel #28
0
class Pull(Atomic):
    def __init__(self, policy_filename=None):
        """
        :param policy_filename: override policy filename
        """
        super(Pull, self).__init__()
        self.policy_filename=policy_filename
        self.be_utils = BackendUtils()

    def pull_image(self):
        if self.args.debug:
            write_out(str(self.args))

        be = self.be_utils.get_backend_from_string(self.args.storage)
        self.args.policy_filename = self.policy_filename
        try:
            be.pull_image(self.args.image, debug=self.args.debug)
        except ValueError as e:
            write_out("Failed: {}".format(e))
            return 1
        return 0
Beispiel #29
0
 def _get_scan_list(self):
     beu = BackendUtils()
     if self.args.images:
         scan_list = beu.get_images()
     elif self.args.containers:
         scan_list = beu.get_containers()
     elif self.args.all:
         scan_list = beu.get_images() + beu.get_containers()
     else:
         scan_list = []
         for scan_target in self.args.scan_targets:
             try:
                 # get_backend_and_container throws a ValueError when it cannot find anything
                 _, scan_obj = beu.get_backend_and_container_obj(scan_target)
             except ValueError:
                 try:
                     # get_backend_and_image throws a ValueError when it cannot find anything
                     _, scan_obj = beu.get_backend_and_image_obj(scan_target)
                 except ValueError:
                     raise ValueError("Unable to locate the container or image '{}' locally. Check the "
                                      "input name for typos or pull the image first.".format(scan_target))
             scan_list.append(scan_obj)
     return scan_list
Beispiel #30
0
 def run(self):
     storage_set = False if self.args.storage is None else True
     storage = _storage if not storage_set else self.args.storage
     be_utils = BackendUtils()
     if self.name:
         try:
             be, con_obj = be_utils.get_backend_and_container_obj(self.name)
             return be.run(con_obj, atomic=self, args=self.args)
         except ValueError:
             pass
     be = be_utils.get_backend_from_string(storage)
     db = DockerBackend()
     img_object = be.has_image(self.image)
     if img_object is None and storage == 'docker':
         self.display("Need to pull %s" % self.image)
         remote_image_obj = db.make_remote_image(self.args.image)
         # If the image has a atomic.type of system, then we need to land
         # this in the ostree backend.  Install it and then start it
         # because this is run
         if remote_image_obj.is_system_type and not storage_set:
             be = be_utils.get_backend_from_string('ostree')
             be_utils.message_backend_change('docker', 'ostree')
             be.install(self.image, self.name)
             con_obj = be.has_container(self.name)
             return be.run(con_obj)
         if self.args.display:
             return 0
         try:
             db.pull_image(self.image, remote_image_obj)
             img_object = db.has_image(self.image)
         except RegistryInspectError:
             raise ValueError("Unable to find image {}".format(self.image))
     if storage == 'ostree':
         if img_object is None:
             be.pull_image(self.args.image, None)
         # For system containers, the run method really needs a container obj
         con_obj = be.has_container(self.name)
         if con_obj is None:
             be.install(self.image, self.name)
         img_object = be.has_container(self.name)
     return be.run(img_object, atomic=self, args=self.args)
Beispiel #31
0
class Containers(Atomic):

    FILTER_KEYWORDS = {
        "container": "id",
        "image": "image_name",
        "command": "command",
        "created": "created",
        "state": "state",
        "runtime": "runtime",
        "backend": "backend.backend"
    }

    def __init__(self):
        super(Containers, self).__init__()
        self.beu = BackendUtils()

    def fstrim(self):
        with AtomicDocker() as client:
            for container in client.containers():
                containerId = container["Id"]
                ret = self._inspect_container(name=containerId)
                pid = ret["State"]["Pid"]
                mp = "/proc/%d/root" % (pid)
                util.write_out("Trimming container id {0}".format(
                    containerId[0:12]))
                util.check_call(["/usr/sbin/fstrim", "-v", mp], stdout=DEVNULL)
        return

    def filter_container_objects(self, con_objs):
        def _walk(_filter_objs, _filter, _value):
            _filtered = []
            for con_obj in _filter_objs:
                it = con_obj
                for i in _filter.split("."):
                    it = getattr(it, i, None)
                if _value.lower() in it.lower():
                    _filtered.append(con_obj)
            return _filtered

        if not self.args.filters:
            return con_objs
        filtered_objs = copy.deepcopy(con_objs)
        for f in self.args.filters:
            cfilter, value = f.split('=', 1)
            cfilter = self.FILTER_KEYWORDS[cfilter]
            filtered_objs = _walk(filtered_objs, cfilter, value)
        return filtered_objs

    def ps_tty(self):
        if self.args.debug:
            util.write_out(str(self.args))
            self.beu.dump_backends()

        container_objects = self._ps()
        if not any([x.running
                    for x in container_objects]) and not self.args.all:
            return 0

        if self.args.quiet:
            for con_obj in container_objects:
                util.write_out(con_obj.id[:12])
            return 0
        if self.args.json:
            util.output_json(self._to_json(container_objects))
            return 0

        if len(container_objects) == 0:
            return 0

        max_container_id = 12 if self.args.truncate else max(
            [len(x.id) for x in container_objects])
        max_image_name = 20 if self.args.truncate else max(
            [len(x.image_name) for x in container_objects])
        max_command = 20 if self.args.truncate else max(
            [len(x.command) for x in container_objects])
        col_out = "{0:2} {1:%s} {2:%s} {3:%s} {4:16} {5:9} {6:10} {7:10}" % (
            max_container_id, max_image_name, max_command)
        if self.args.heading:
            util.write_out(
                col_out.format(" ", "CONTAINER ID", "IMAGE", "COMMAND",
                               "CREATED", "STATE", "BACKEND", "RUNTIME"))
        for con_obj in container_objects:
            indicator = ""
            if con_obj.vulnerable:
                if util.is_python2:
                    indicator = indicator + self.skull + " "
                else:
                    indicator = indicator + str(self.skull, "utf-8") + " "
            util.write_out(
                col_out.format(indicator, con_obj.id[0:max_container_id],
                               con_obj.image_name[0:max_image_name],
                               con_obj.command[0:max_command],
                               con_obj.created[0:16], con_obj.state[0:9],
                               con_obj.backend.backend[0:10],
                               con_obj.runtime[0:10]))

    def ps(self):
        container_objects = self._ps()
        return self._to_json(container_objects)

    def _ps(self):
        def _check_filters():
            if not self.args.filters:
                return True
            for f in self.args.filters:
                _filter, _ = f.split('=', 1)
                keywords = list(self.FILTER_KEYWORDS.keys())
                if _filter not in keywords:
                    raise ValueError("The filter {} is not valid.  "
                                     "Please choose from {}".format(
                                         _filter, keywords))

        _check_filters()
        containers = self.filter_container_objects(self.beu.get_containers())
        self._mark_vulnerable(containers)
        if self.args.all:
            return containers
        return [x for x in containers if x.running]

    @staticmethod
    def _to_json(con_objects):
        containers = []
        for con_obj in con_objects:
            _con = {
                'id': con_obj.id,
                'image_id': con_obj.image_id,
                'image_name': con_obj.image_name,
                'command': con_obj.command,
                'created': con_obj.created,
                'state': con_obj.state,
                'backend': con_obj.backend.backend,
                'runtime': con_obj.runtime,
                'vulnerable': con_obj.vulnerable,
                'running': con_obj.running
            }
            containers.append(_con)
        return containers

    def delete(self):
        if self.args.debug:
            util.write_out(str(self.args))
            self.beu.dump_backends()

        if (len(self.args.containers) > 0
                and self.args.all) or (len(self.args.containers) < 1
                                       and not self.args.all):
            raise ValueError(
                "You must select --all or provide a list of images to delete.")

        if self.args.all:
            if self.args.storage:
                be = self.beu.get_backend_from_string(self.args.storage)
                container_objects = be.get_containers()
            else:
                container_objects = self.beu.get_containers()
        else:
            container_objects = []
            for con in self.args.containers:
                _, con_obj = self.beu.get_backend_and_container_obj(
                    con,
                    str_preferred_backend=self.args.storage or storage,
                    required=True if self.args.storage else False)
                container_objects.append(con_obj)

        if len(container_objects) == 0:
            raise ValueError("No containers to delete")
        four_col = "   {0:12} {1:20} {2:25} {3:10}"
        if not self.args.assumeyes:
            util.write_out("Do you wish to delete the following images?\n")
        else:
            util.write_out("The following containers will be deleted.\n")
        util.write_out(four_col.format("ID", "NAME", 'IMAGE_NAME', "STORAGE"))
        for con in container_objects:
            util.write_out(
                four_col.format(con.id[0:12], con.name[0:20],
                                con.image_name[0:25], con.backend.backend))
        if not self.args.assumeyes:
            confirm = util.input("\nConfirm (y/N) ")
            confirm = confirm.strip().lower()
            if not confirm in ['y', 'yes']:
                util.write_err("User aborted delete operation for {}".format(
                    self.args.containers or "all containers"))
                sys.exit(2)

        for del_con in container_objects:
            try:
                del_con.backend.delete_container(del_con.id,
                                                 force=self.args.force)
            except APIError as e:
                util.write_err("Failed to delete container {}: {}".format(
                    con.id, e))
        return 0

    def _mark_vulnerable(self, containers):
        assert isinstance(containers, list)
        vulnerable_uuids = self.get_vulnerable_ids()
        for con in containers:
            if con.id in vulnerable_uuids:
                con.vulnerable = True

    def update(self):
        if self.syscontainers.get_checkout(self.args.container):
            return self.syscontainers.update_container(self.args.container,
                                                       self.args.setvalues,
                                                       self.args.rebase)
        raise ValueError("System container '%s' is not installed" %
                         self.args.container)

    def rollback(self):
        util.write_out("Attempting to roll back system container: %s" %
                       self.args.container)
        self.syscontainers.rollback(self.args.container)
Beispiel #32
0
class Containers(Atomic):

    FILTER_KEYWORDS = {
        "container": "id",
        "image": "image_name",
        "command": "command",
        "created": "created",
        "state": "state",
        "runtime": "runtime",
        "backend": "backend.backend"
    }

    def __init__(self):
        super(Containers, self).__init__()
        self.beu = BackendUtils()

    def fstrim(self):
        with AtomicDocker() as client:
            for container in client.containers():
                containerId = container["Id"]
                ret = self._inspect_container(name=containerId)
                pid = ret["State"]["Pid"]
                mp = "/proc/%d/root" % (pid)
                util.write_out("Trimming container id {0}".format(
                    containerId[0:12]))
                util.check_call(["/usr/sbin/fstrim", "-v", mp], stdout=DEVNULL)
        return

    def filter_container_objects(self, con_objs):
        def _walk(_filter_objs, _filter, _value):
            _filtered = []
            for con_obj in _filter_objs:
                it = con_obj
                for i in _filter.split("."):
                    it = getattr(it, i, None)
                if _value.lower() in it.lower():
                    _filtered.append(con_obj)
            return _filtered

        if not self.args.filter:
            return con_objs
        for f in self.args.filter:
            cfilter, value = f.split('=', 1)
            cfilter = self.FILTER_KEYWORDS[cfilter]
            con_objs = _walk(con_objs, cfilter, value)
        return con_objs

    def ps_tty(self):
        if self.args.debug:
            util.write_out(str(self.args))
            self.beu.dump_backends()

        container_objects = self._ps()
        # If we were not asked for json output, return out with no output
        # when there are no applicable container objects
        if not self.args.json:
            if len(container_objects) == 0:
                return 0

            if not any([x.running
                        for x in container_objects]) and not self.args.all:
                return 0

        # Set to 12 when truncate
        if self.args.truncate:
            max_container_id = 12
        # Otherwise set to the max, falling back to 0
        else:
            max_container_id = max([len(x.id) for x in container_objects]
                                   or [0])

        # Quiet supersedes json output
        if self.args.quiet:
            for con_obj in container_objects:
                util.write_out(con_obj.id[0:max_container_id])
            return 0

        if self.args.json:
            util.output_json(self._to_json(container_objects))
            return 0

        max_image_name = 20 if self.args.truncate else max(
            [len(x.image_name) for x in container_objects])
        if self.args.truncate:
            max_command = 10
        else:
            _max_command = max([len(x.command) for x in container_objects])
            max_command = _max_command if _max_command > 9 else 10

        max_container_name = 10 if self.args.truncate else max(
            [len(x.name) for x in container_objects])

        col_out = "{0:2} {1:%s} {2:%s} {3:%s} {4:%s} {5:16} {6:10} {7:10} {8:10}" % (
            max_container_id, max_image_name, max_container_name, max_command)
        if self.args.heading:
            util.write_out(
                col_out.format(" ", "CONTAINER ID", "IMAGE", "NAME", "COMMAND",
                               "CREATED", "STATE", "BACKEND", "RUNTIME"))
        for con_obj in container_objects:
            indicator = ""
            if con_obj.vulnerable:
                if util.is_python2:
                    indicator = indicator + self.skull + " "
                else:
                    indicator = indicator + str(self.skull, "utf-8") + " "
            util.write_out(
                col_out.format(indicator, con_obj.id[0:max_container_id],
                               con_obj.image_name[0:max_image_name],
                               con_obj.name[0:max_container_name],
                               str(con_obj.command)[0:max_command],
                               con_obj.created[0:16], con_obj.state[0:10],
                               con_obj.backend.backend[0:10],
                               con_obj.runtime[0:10]))

    def ps(self):
        container_objects = self._ps()
        return self._to_json(container_objects)

    def _ps(self):
        def _check_filters():
            if not self.args.filter:
                return True
            for f in self.args.filter:
                _filter, _ = f.split('=', 1)
                keywords = list(self.FILTER_KEYWORDS.keys())
                if _filter not in keywords:
                    raise ValueError("The filter {} is not valid.  "
                                     "Please choose from {}".format(
                                         _filter, keywords))

        _check_filters()
        containers = self.filter_container_objects(self.beu.get_containers())
        self._mark_vulnerable(containers)
        if self.args.all:
            return containers
        return [x for x in containers if x.running]

    @staticmethod
    def _to_json(con_objects):
        containers = []
        for con_obj in con_objects:
            _con = {
                'id': con_obj.id,
                'image_id': con_obj.image,
                'image_name': con_obj.image_name,
                'name': con_obj.name,
                'command': con_obj.command,
                'created': con_obj.created,
                'state': con_obj.state,
                'backend': con_obj.backend.backend,
                'runtime': con_obj.runtime,
                'vulnerable': con_obj.vulnerable,
                'running': con_obj.running
            }
            containers.append(_con)
        return containers

    def delete(self):
        if self.args.debug:
            util.write_out(str(self.args))
            self.beu.dump_backends()

        if (len(self.args.containers) > 0
                and self.args.all) or (len(self.args.containers) < 1
                                       and not self.args.all):
            raise ValueError(
                "You must select --all or provide a list of images to delete.")

        if self.args.all:
            if self.args.storage:
                be = self.beu.get_backend_from_string(self.args.storage)
                container_objects = be.get_containers()
            else:
                container_objects = self.beu.get_containers()
        else:
            container_objects = []
            for con in self.args.containers:
                _, con_obj = self.beu.get_backend_and_container_obj(
                    con,
                    str_preferred_backend=self.args.storage or storage,
                    required=True if self.args.storage else False)
                container_objects.append(con_obj)

        if len(container_objects) == 0:
            raise ValueError("No containers to delete")
        four_col = "   {0:12} {1:20} {2:25} {3:10}"
        if not self.args.assumeyes:
            util.write_out("Do you wish to delete the following images?\n")
        else:
            util.write_out("The following containers will be deleted.\n")
        util.write_out(four_col.format("ID", "NAME", 'IMAGE_NAME', "STORAGE"))
        for con in container_objects:
            util.write_out(
                four_col.format(con.id[0:12], con.name[0:20],
                                con.image_name[0:25], con.backend.backend))
        if not self.args.assumeyes:
            confirm = util.input("\nConfirm (y/N) ")
            confirm = confirm.strip().lower()
            if not confirm in ['y', 'yes']:
                util.write_err("User aborted delete operation for {}".format(
                    self.args.containers or "all containers"))
                sys.exit(2)

        for del_con in container_objects:
            try:
                del_con.backend.delete_container(del_con.id,
                                                 force=self.args.force)
            except APIError as e:
                util.write_err("Failed to delete container {}: {}".format(
                    con.id, e))
        return 0

    def _mark_vulnerable(self, containers):
        assert isinstance(containers, list)
        vulnerable_uuids = self.get_vulnerable_ids()
        for con in containers:
            if con.id in vulnerable_uuids:
                con.vulnerable = True

    def update_all_containers(self):
        preupdate_containers = self.syscontainers.get_containers()
        could_not_update = {}
        for i in preupdate_containers:
            name = i['Names'][0]
            util.write_out("Checking container {}...".format(name))
            try:
                self.syscontainers.update_container(name, controlled=True)
            except:  # pylint: disable=bare-except
                could_not_update[name] = True

        postupdate_containers = self.syscontainers.get_containers()

        images_by_name = {
            i['RepoTags'][0]: i
            for i in self.syscontainers.get_system_images()
        }
        preupdate_containers_by_name = {
            i['Names'][0]: i
            for i in preupdate_containers
        }
        postupdate_containers_by_name = {
            i['Names'][0]: i
            for i in postupdate_containers
        }

        def get_image_id(c):
            return c['ImageId'] if 'ImageId' in c else c['ImageID']

        def colored(line, color):
            if sys.stdout.isatty():
                return "\x1b[1;%dm%s\x1b[0m" % (color, line)
            else:
                return line

        def get_status(container_name, pre_id, post_id, image_id):
            COLOR_RED = 31
            COLOR_GREEN = 32
            COLOR_YELLOW = 33

            if container_name in could_not_update:
                return "Failed", COLOR_RED
            if image_id == None:
                return "Unknown", COLOR_YELLOW
            if post_id == image_id and image_id != pre_id:
                return "Updated now", COLOR_GREEN
            if post_id == image_id and image_id == pre_id:
                return "Updated", COLOR_GREEN
            return "Not updated", COLOR_RED

        cols = "{0:15} {1:32} {2:32} {3:32} {4:15}"

        util.write_out("\nSUMMARY\n")
        util.write_out(
            cols.format("Container", "Image ID before update",
                        "Image ID after update", "Latest image ID", "Status"))
        for cnt in preupdate_containers_by_name.keys():
            pre_version = get_image_id(preupdate_containers_by_name[cnt])
            post_version = get_image_id(postupdate_containers_by_name[cnt])
            img_name = img_id = None
            try:
                img_name = preupdate_containers_by_name[cnt]['Image']
                img_id = get_image_id(images_by_name[img_name])
            except KeyError:
                pass
            status, color = get_status(cnt, pre_version, post_version, img_id)
            colored_status = colored(status[:15], color)
            util.write_out(
                cols.format(cnt[:15], pre_version[:32], post_version[:32],
                            (img_id or "<NOT FOUND>")[:32], colored_status))

    def update(self):
        if self.args.all:
            if self.args.container is not None:
                raise ValueError(
                    "Incompatible options specified.  --all doesn't support a container name"
                )
            if self.args.setvalues or self.args.rebase:
                raise ValueError(
                    "Incompatible options specified.  --all doesn't support --set or --rebase"
                )

            return self.update_all_containers()

        if self.args.container is None:
            raise ValueError(
                "Too few arguments.  Please specify the container")

        if self.syscontainers.get_checkout(self.args.container):
            return self.syscontainers.update_container(self.args.container,
                                                       self.args.setvalues,
                                                       self.args.rebase)
        raise ValueError("System container '%s' is not installed" %
                         self.args.container)

    def rollback(self):
        util.write_out("Attempting to roll back system container: %s" %
                       self.args.container)
        self.syscontainers.rollback(self.args.container)
Beispiel #33
0
    def delete_image(self):
        """
        Mark given image(s) for deletion from registry
        :return: 0 if all images marked for deletion, otherwise 2 on any failure
        """
        if self.args.debug:
            util.write_out(str(self.args))

        if len(self.args.delete_targets) > 0 and self.args.all:
            raise ValueError(
                "You must select --all or provide a list of images to delete.")

        beu = BackendUtils()
        delete_objects = []

        # We need to decide on new returns for dbus because we now check image
        # validity prior to executing the delete.  If there is going to be a
        # failure, it will be here.
        #
        # The failure here is basically that it couldnt verify/find the image.

        if self.args.all:
            delete_objects = beu.get_images(get_all=True)
        else:
            for image in self.args.delete_targets:
                _, img_obj = beu.get_backend_and_image_obj(
                    image,
                    str_preferred_backend=self.args.storage or storage,
                    required=True if self.args.storage else False)
                delete_objects.append(img_obj)

        if self.args.remote:
            return self._delete_remote(self.args.delete_targets)

        _image_names = []
        for del_obj in delete_objects:
            if del_obj.repotags:
                _image_names.append(len(del_obj.repotags[0]))
            else:
                _image_names.append(len(del_obj.id))
        max_img_name = max(_image_names) + 2

        if not self.args.assumeyes:
            util.write_out("Do you wish to delete the following images?\n")
            two_col = "   {0:" + str(max_img_name) + "} {1}"
            util.write_out(two_col.format("IMAGE", "STORAGE"))
            for del_obj in delete_objects:
                image = None if not del_obj.repotags else del_obj.repotags[0]
                if image is None or "<none>" in image:
                    image = del_obj.id[0:12]
                util.write_out(two_col.format(image, del_obj.backend.backend))
            confirm = util.input("\nConfirm (y/N) ")
            confirm = confirm.strip().lower()
            if not confirm in ['y', 'yes']:
                util.write_err("User aborted delete operation for {}".format(
                    self.args.delete_targets))
                sys.exit(2)

        # Perform the delete
        for del_obj in delete_objects:
            del_obj.backend.delete_image(del_obj.input_name,
                                         force=self.args.force)

        # We need to return something here for dbus
        return
Beispiel #34
0
class Info(Atomic):
    def __init__(self):
        super(Info, self).__init__()
        self.beu = BackendUtils()

    def version(self):
        self._version(util.write_out)

    def _version(self, write_func):
        layer_objects = self.get_layer_objects()
        max_version_len = max([len(x.long_version) for x in layer_objects])
        max_version_len = max_version_len if max_version_len > 9 else 9
        max_img_len = len(max([y for x in layer_objects for y in x.repotags], key=len)) + 9
        max_img_len = max_img_len if max_img_len > 12 else 12
        col_out = "{0:" + str(max_img_len) + "} {1:" + str(max_version_len) + "} {2:10}"

        write_func(col_out.format("IMAGE NAME", "VERSION", "IMAGE ID"))
        for layer in layer_objects:
            for int_img_name in range(len(layer.repotags)):
                version = layer.long_version if int_img_name < 1 else ""
                iid = layer.id[:12] if int_img_name < 1 else ""
                space = "" if int_img_name < 1 else "  Tag: "
                write_func(col_out.format(space + layer.repotags[int_img_name], version, iid))
                write_func("")

    def get_layer_objects(self):
        _, img_obj = self.beu.get_backend_and_image_obj(self.image, str_preferred_backend=self.args.storage)
        return img_obj.layers

    def dbus_version(self):
        layer_objects = self.get_layer_objects()
        versions = []
        for layer in layer_objects:
            versions.append({"Image": layer.repotags, "Version": layer.long_version, "iid": layer.id})
        return versions

    def info_tty(self):
        if self.args.debug:
            util.write_out(str(self.args))
        util.write_out(self.info())

    def info(self):
        """
        Retrieve and print all LABEL information for a given image.
        """

        if self.args.storage == 'ostree' and self.args.force:
            # Ostree and remote combos are illegal
            raise ValueError("The --remote option cannot be used with the 'ostree' storage option.")

        if self.args.force:
            # The user wants information on a remote image
            be = self.beu.get_backend_from_string(self.args.storage)
            img_obj = be.make_remote_image(self.image)
        else:
            # The image is local
            be, img_obj = self.beu.get_backend_and_image_obj(self.image, str_preferred_backend=self.args.storage)

        with closing(StringIO()) as buf:
            try:
                info_name = img_obj.fq_name
            except RegistryInspectError:
                info_name = img_obj.input_name
            buf.write("Image Name: {}\n".format(info_name))
            buf.writelines(sorted(["{}: {}\n".format(k, v) for k,v in list(img_obj.labels.items())]))
            if img_obj.template_variables_set:
                buf.write("\n\nTemplate variables with default value, but overridable with --set:\n")
                buf.writelines(["{}: {}\n".format(k, v) for k,v in
                                list(sorted(img_obj.template_variables_set.items()))])
            if img_obj.template_variables_unset:
                buf.write("\n\nTemplate variables that has no default value, and must be set with --set:\n")
                buf.writelines(["{}: {}\n".format(k, v) for k,v in
                                list(sorted(img_obj.template_variables_unset.items()))])
            return buf.getvalue()
Beispiel #35
0
 def update(self):
     beu = BackendUtils()
     be, img_obj = beu.get_backend_and_image_obj(self.image, str_preferred_backend=self.args.storage)
     be.update(img_obj.input_name, force=self.args.force)
Beispiel #36
0
    def install(self):
        if self.args.debug:
            util.write_out(str(self.args))
        storage_set = False if self.args.storage is None else True
        storage = _storage if not storage_set else self.args.storage
        args_system = getattr(self.args, 'system', None)
        args_user = getattr(self.args, 'user', None)
        if (args_system or args_user) and storage != 'ostree' and storage_set:
            raise ValueError("The --system and --user options are only available for the 'ostree' storage.")
        be_utils = BackendUtils()
        try:
            # Check to see if the container already exists
            _, _ = be_utils.get_backend_and_container_obj(self.name)
            raise ValueError("A container '%s' is already present" % self.name)
        except ValueError:
            pass

        if self.user:
            if not util.is_user_mode():
                raise ValueError("--user does not work for privileged user")
            return self.syscontainers.install_user_container(self.image, self.name)
        if self.ostree_uri(self.image):
            return self.syscontainers.install(self.image, self.name)
        # Check if image exists
        str_backend = 'ostree' if self.args.system else self.args.storage or storage
        be = be_utils.get_backend_from_string(str_backend)
        img_obj = be.has_image(self.args.image)
        if img_obj and img_obj.is_system_type:
            be = be_utils.get_backend_from_string('ostree')

        if img_obj is None:
            # Unable to find the image locally, look remotely
            remote_image_obj = be.make_remote_image(self.args.image)
            # We found an atomic.type of system, therefore install it onto the ostree
            # backend
            if remote_image_obj.is_system_type and not storage_set:
                be_utils.message_backend_change('docker', 'ostree')
                be = be_utils.get_backend_from_string('ostree')
            be.pull_image(self.args.image, remote_image_obj, debug=self.args.debug)
            img_obj = be.has_image(self.image)

        if be.backend is not 'docker':
            if OSTREE_PRESENT and self.args.setvalues and not self.user and not self.system:
                raise ValueError("--set is valid only when used with --system or --user")

            # We need to fix this long term and get ostree
            # using the backend approach vs the atomic args
            be.syscontainers.set_args(self.args)
            return be.install(self.image, self.name)

        installation = None
        if storage == 'docker' and not args_system:
            if self.args.system_package == 'build':
                raise ValueError("'--system-package=build' is not supported for docker backend")
            installation = be.rpm_install(img_obj, self.name)

        install_args = img_obj.get_label('INSTALL')

        if installation or install_args:
            try:
                name = img_obj.fq_name
            except RegistryInspectError:
                name = img_obj.input_name
            install_data_content = {
                'id': img_obj.id,
                "container_name": self.name,
                'install_date': strftime("%Y-%m-%d %H:%M:%S", gmtime())
            }
            if installation:
                # let's fail the installation if rpm for this image is already installed
                if util.InstallData.image_installed(img_obj):
                    raise ValueError("Image {} is already installed.".format(self.image))
                install_data_content["rpm_installed_files"] = installation.installed_files
                rpm_nvra = re.sub(r"\.rpm$", "", installation.original_rpm_name)
                install_data_content["system_package_nvra"] = rpm_nvra
            install_data = {name: install_data_content}

        if not install_args:
            return 0
        install_args = install_args.split()
        cmd = self.sub_env_strings(self.gen_cmd(install_args + self.quote(self.args.args)))
        self.display(cmd)

        if not self.args.display:
            result = util.check_call(cmd)
            if result == 0:
                if installation or install_args:
                    # Only write the install data if the installation worked.
                    util.InstallData.write_install_data(install_data)
            return result
Beispiel #37
0
    def install(self):
        if self.args.debug:
            util.write_out(str(self.args))
        storage_set = False if self.args.storage is None else True
        storage = _storage if not storage_set else self.args.storage
        args_system = getattr(self.args, 'system', None)
        args_user = getattr(self.args, 'user', None)
        if (args_system or args_user) and storage != 'ostree' and storage_set:
            raise ValueError(
                "The --system and --user options are only available for the 'ostree' storage."
            )
        be_utils = BackendUtils()
        try:
            # Check to see if the container already exists
            _, _ = be_utils.get_backend_and_container_obj(self.name)
            raise ValueError("A container '%s' is already present" % self.name)
        except ValueError:
            pass

        if self.user:
            if not util.is_user_mode():
                raise ValueError("--user does not work for privileged user")
            return self.syscontainers.install_user_container(
                self.image, self.name)
        if self.ostree_uri(self.image):
            return self.syscontainers.install(self.image, self.name)
        # Check if image exists
        str_backend = 'ostree' if self.args.system else self.args.storage or storage
        be = be_utils.get_backend_from_string(str_backend)
        img_obj = be.has_image(self.args.image)
        if img_obj and img_obj.is_system_type:
            be = be_utils.get_backend_from_string('ostree')

        if img_obj is None:
            # Unable to find the image locally, look remotely
            remote_image_obj = be.make_remote_image(self.args.image)
            # We found an atomic.type of system, therefore install it onto the ostree
            # backend
            if remote_image_obj.is_system_type and not storage_set:
                be_utils.message_backend_change('docker', 'ostree')
                be = be_utils.get_backend_from_string('ostree')
            be.pull_image(self.args.image,
                          remote_image_obj,
                          debug=self.args.debug)
            img_obj = be.has_image(self.image)

        if be.backend is not 'docker':
            if OSTREE_PRESENT and self.args.setvalues and not self.user and not self.system:
                raise ValueError(
                    "--set is valid only when used with --system or --user")

            # We need to fix this long term and get ostree
            # using the backend approach vs the atomic args
            be.syscontainers.set_args(self.args)
            return be.install(self.image, self.name)

        installation = None
        if storage == 'docker' and not args_system:
            if self.args.system_package == 'build':
                raise ValueError(
                    "'--system-package=build' is not supported for docker backend"
                )
            installation = be.rpm_install(img_obj, self.name)

        install_args = img_obj.get_label('INSTALL')

        if installation or install_args:
            try:
                name = img_obj.fq_name
            except RegistryInspectError:
                name = img_obj.input_name
            install_data_content = {
                'id': img_obj.id,
                "container_name": self.name,
                'install_date': strftime("%Y-%m-%d %H:%M:%S", gmtime())
            }
            if installation:
                # let's fail the installation if rpm for this image is already installed
                if util.InstallData.image_installed(img_obj):
                    raise ValueError("Image {} is already installed.".format(
                        self.image))
                install_data_content[
                    "rpm_installed_files"] = installation.installed_files
                rpm_nvra = re.sub(r"\.rpm$", "",
                                  installation.original_rpm_name)
                install_data_content["system_package_nvra"] = rpm_nvra
            install_data = {name: install_data_content}
            util.InstallData.write_install_data(install_data)

        if not install_args:
            return 0
        install_args = install_args.split()
        cmd = self.sub_env_strings(
            self.gen_cmd(install_args + self.quote(self.args.args)))
        self.display(cmd)

        if not self.args.display:
            return util.check_call(cmd)
Beispiel #38
0
 def __init__(self):
     super(Info, self).__init__()
     self.beu = BackendUtils()
Beispiel #39
0
class Info(Atomic):
    def __init__(self):
        super(Info, self).__init__()
        self.beu = BackendUtils()

    def version(self):
        self._version(util.write_out)

    def _version(self, write_func):
        layer_objects = self.get_layer_objects()
        max_version_len = max([len(x.long_version) for x in layer_objects])
        max_version_len = max_version_len if max_version_len > 9 else 9
        max_img_len = len(
            max([y for x in layer_objects for y in x.repotags], key=len)) + 9
        max_img_len = max_img_len if max_img_len > 12 else 12
        col_out = "{0:" + str(max_img_len) + "} {1:" + str(
            max_version_len) + "} {2:10}"

        write_func(col_out.format("IMAGE NAME", "VERSION", "IMAGE ID"))
        for layer in layer_objects:
            for int_img_name in range(len(layer.repotags)):
                version = layer.long_version if int_img_name < 1 else ""
                iid = layer.id[:12] if int_img_name < 1 else ""
                space = "" if int_img_name < 1 else "  Tag: "
                write_func(
                    col_out.format(space + layer.repotags[int_img_name],
                                   version, iid))
                write_func("")

    def get_layer_objects(self):
        _, img_obj = self.beu.get_backend_and_image(
            self.image, str_preferred_backend=self.args.storage)
        return img_obj.layers

    def dbus_version(self):
        layer_objects = self.get_layer_objects()
        versions = []
        for layer in layer_objects:
            versions.append({
                "Image": layer.repotags,
                "Version": layer.long_version,
                "iid": layer.id
            })
        return versions

    def info_tty(self):
        if self.args.debug:
            util.write_out(str(self.args))
        util.write_out(self.info())

    def info(self):
        """
        Retrieve and print all LABEL information for a given image.
        """

        if self.args.storage == 'ostree' and self.args.force:
            # Ostree and remote combos are illegal
            raise ValueError(
                "The --remote option cannot be used with the 'ostree' storage option."
            )

        if self.args.force:
            # The user wants information on a remote image
            be = self.beu.get_backend_from_string(self.args.storage)
            img_obj = be.make_remote_image(self.image)
        else:
            # The image is local
            be, img_obj = self.beu.get_backend_and_image(
                self.image, str_preferred_backend=self.args.storage)

        with closing(StringIO()) as buf:
            try:
                info_name = img_obj.fq_name
            except RegistryInspectError:
                info_name = img_obj.input_name
            buf.write("Image Name: {}\n".format(info_name))
            buf.writelines(
                sorted([
                    "{}: {}\n".format(k, v)
                    for k, v in list(img_obj.labels.items())
                ]))
            if img_obj.template_variables_set:
                buf.write(
                    "\n\nTemplate variables with default value, but overridable with --set:\n"
                )
                buf.writelines([
                    "{}: {}\n".format(k, v) for k, v in list(
                        sorted(img_obj.template_variables_set.items()))
                ])
            if img_obj.template_variables_unset:
                buf.write(
                    "\n\nTemplate variables that has no default value, and must be set with --set:\n"
                )
                buf.writelines([
                    "{}: {}\n".format(k, v) for k, v in list(
                        sorted(img_obj.template_variables_unset.items()))
                ])
            return buf.getvalue()
Beispiel #40
0
class Containers(Atomic):

    FILTER_KEYWORDS= {"container": "id", "image": "image_name", "command": "command",
                      "created": "created", "state": "state", "runtime": "runtime", "backend" : "backend.backend"}

    def __init__(self):
        super(Containers, self).__init__()
        self.beu = BackendUtils()

    def fstrim(self):
        with AtomicDocker() as client:
            for container in client.containers():
                containerId = container["Id"]
                ret = self._inspect_container(name=containerId)
                pid = ret["State"]["Pid"]
                mp = "/proc/%d/root" % (pid)
                util.write_out("Trimming container id {0}".format(containerId[0:12]))
                util.check_call(["/usr/sbin/fstrim", "-v", mp], stdout=DEVNULL)
        return

    def filter_container_objects(self, con_objs):
        def _walk(_filter_objs, _filter, _value):
            _filtered = []
            for con_obj in _filter_objs:
                it = con_obj
                for i in _filter.split("."):
                    it = getattr(it, i, None)
                if _value.lower() in it.lower():
                    _filtered.append(con_obj)
            return _filtered

        if not self.args.filter:
            return con_objs
        for f in self.args.filter:
            cfilter, value = f.split('=', 1)
            cfilter = self.FILTER_KEYWORDS[cfilter]
            con_objs = _walk(con_objs, cfilter, value)
        return con_objs

    def ps_tty(self):
        if self.args.debug:
            util.write_out(str(self.args))
            self.beu.dump_backends()


        container_objects = self._ps()
        # If we were not asked for json output, return out with no output
        # when there are no applicable container objects
        if not self.args.json:
            if len(container_objects) == 0:
                return 0

            if not any([x.running for x in container_objects]) and not self.args.all:
                return 0

        # Set to 12 when truncate
        if self.args.truncate:
            max_container_id = 12
        # Otherwise set to the max, falling back to 0
        else:
            max_container_id = max([len(x.id) for x in container_objects] or [0])

        # Quiet supersedes json output
        if self.args.quiet:
            for con_obj in container_objects:
                util.write_out(con_obj.id[0:max_container_id])
            return 0

        if self.args.json:
            util.output_json(self._to_json(container_objects))
            return 0

        max_image_name = 20 if self.args.truncate else max([len(x.image_name) for x in container_objects])
        if self.args.truncate:
            max_command = 10
        else:
            _max_command = max([len(x.command) for x in container_objects])
            max_command = _max_command if _max_command > 9 else 10

        max_container_name = 10 if self.args.truncate else max([len(x.name) for x in container_objects])

        col_out = "{0:2} {1:%s} {2:%s} {3:%s} {4:%s} {5:16} {6:10} {7:10} {8:10}" % (max_container_id,
                                                                                     max_image_name,
                                                                                     max_container_name,
                                                                                     max_command)
        if self.args.heading:
            util.write_out(col_out.format(" ",
                                          "CONTAINER ID",
                                          "IMAGE",
                                          "NAME",
                                          "COMMAND",
                                          "CREATED",
                                          "STATE",
                                          "BACKEND",
                                          "RUNTIME"))
        for con_obj in container_objects:
            indicator = ""
            if con_obj.vulnerable:
                if util.is_python2:
                    indicator = indicator + self.skull + " "
                else:
                    indicator = indicator + str(self.skull, "utf-8") + " "
            util.write_out(col_out.format(indicator,
                                          con_obj.id[0:max_container_id],
                                          con_obj.image_name[0:max_image_name],
                                          con_obj.name[0:max_container_name],
                                          str(con_obj.command)[0:max_command],
                                          con_obj.created[0:16],
                                          con_obj.state[0:10],
                                          con_obj.backend.backend[0:10],
                                          con_obj.runtime[0:10]))

    def ps(self):
        container_objects = self._ps()
        return self._to_json(container_objects)

    def _ps(self):
        def _check_filters():
            if not self.args.filter:
                return True
            for f in self.args.filter:
                _filter, _ = f.split('=', 1)
                keywords = list(self.FILTER_KEYWORDS.keys())
                if _filter not in keywords:
                    raise ValueError("The filter {} is not valid.  "
                                     "Please choose from {}".format(_filter, keywords))
        _check_filters()
        containers = self.filter_container_objects(self.beu.get_containers())
        self._mark_vulnerable(containers)
        if self.args.all:
            return containers
        return [x for x in containers if x.running]

    @staticmethod
    def _to_json(con_objects):
        containers = []
        for con_obj in con_objects:
            _con = {'id': con_obj.id,
                    'image_id': con_obj.image,
                    'image_name': con_obj.image_name,
                    'name': con_obj.name,
                    'command': con_obj.command,
                    'created': con_obj.created,
                    'state': con_obj.state,
                    'backend': con_obj.backend.backend,
                    'runtime': con_obj.runtime,
                    'vulnerable': con_obj.vulnerable,
                    'running': con_obj.running
                    }
            containers.append(_con)
        return containers

    def delete(self):
        if self.args.debug:
            util.write_out(str(self.args))
            self.beu.dump_backends()

        if (len(self.args.containers) > 0 and self.args.all) or (len(self.args.containers) < 1 and not self.args.all):
            raise ValueError("You must select --all or provide a list of images to delete.")

        if self.args.all:
            if self.args.storage:
                be = self.beu.get_backend_from_string(self.args.storage)
                container_objects = be.get_containers()
            else:
                container_objects = self.beu.get_containers()
        else:
            container_objects = []
            for con in self.args.containers:
                _, con_obj = self.beu.get_backend_and_container_obj(con, str_preferred_backend=self.args.storage or storage, required=True if self.args.storage else False)
                container_objects.append(con_obj)

        if len(container_objects) == 0:
            raise ValueError("No containers to delete")
        four_col = "   {0:12} {1:20} {2:25} {3:10}"
        if not self.args.assumeyes:
            util.write_out("Do you wish to delete the following images?\n")
        else:
            util.write_out("The following containers will be deleted.\n")
        util.write_out(four_col.format("ID", "NAME", 'IMAGE_NAME', "STORAGE"))
        for con in container_objects:
            util.write_out(four_col.format(con.id[0:12], con.name[0:20], con.image_name[0:25], con.backend.backend))
        if not self.args.assumeyes:
            confirm = util.input("\nConfirm (y/N) ")
            confirm = confirm.strip().lower()
            if not confirm in ['y', 'yes']:
                util.write_err("User aborted delete operation for {}".format(self.args.containers or "all containers"))
                sys.exit(2)

        for del_con in container_objects:
            try:
                del_con.backend.delete_container(del_con.id, force=self.args.force)
            except APIError as e:
                util.write_err("Failed to delete container {}: {}".format(con.id, e))
        return 0

    def _mark_vulnerable(self, containers):
        assert isinstance(containers, list)
        vulnerable_uuids = self.get_vulnerable_ids()
        for con in containers:
            if con.id in vulnerable_uuids:
                con.vulnerable = True

    def update(self):
        if self.syscontainers.get_checkout(self.args.container):
            return self.syscontainers.update_container(self.args.container, self.args.setvalues, self.args.rebase)
        raise ValueError("System container '%s' is not installed" % self.args.container)

    def rollback(self):
        util.write_out("Attempting to roll back system container: %s" % self.args.container)
        self.syscontainers.rollback(self.args.container)
Beispiel #41
0
 def __init__(self):
     super(Containers, self).__init__()
     self.beu = BackendUtils()
Beispiel #42
0
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")
Beispiel #43
0
 def __init__(self):
     super(Info, self).__init__()
     self.beu = BackendUtils()
Beispiel #44
0
 def __init__(self):
     super(Verify, self).__init__()
     self.debug = False
     self.backend_utils = BackendUtils()
Beispiel #45
0
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.debug:
            util.write_out(str(self.args))
        local_layers, remote_layers = self._verify()
        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(self.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")

    def verify_dbus(self):
        local_layers, remote_layers = self._verify()
        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):
        be, img_obj = self.backend_utils.get_backend_and_image_obj(self.image, self.args.storage)
        remote_img_name  = "{}:latest".format(util.Decompose(img_obj.fq_name).no_tag)
        remote_img_obj = be.make_remote_image(remote_img_name)
        return 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, _ = util.Decompose(self.get_fq_image_name(_match['RepoTags'][0])).all
            tag = "latest"
            ri = RegistryInspect(registry=registry, repo=repo, image=image, tag=tag, debug=self.debug)
            ri.ping()
            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.rc.manifest_json.get("config", None).get("digest", None))
                _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")

    def verify_system_image(self):
        manifest = self.syscontainers.get_manifest(self.image)
        name = json.loads(manifest).get('Name', self.image)
        if manifest:
            layers = SystemContainers.get_layers_from_manifest(manifest)
        else:
            layers = [self.image]
        if not getattr(self.args,"no_validate", False):
            self.validate_system_image_manifests(layers)

        if not manifest:
            return
        remote = True
        try:
            remote_manifest = self.syscontainers.get_manifest(self.image, remote=True)
            remote_layers = SystemContainers.get_layers_from_manifest(remote_manifest)
        except subprocess.CalledProcessError:
            remote_layers = []
            remote = False

        if hasattr(itertools, 'izip_longest'):
            zip_longest = getattr(itertools, 'izip_longest')
        else:
            zip_longest = getattr(itertools, 'zip_longest')

        images = []
        for local, remote in zip_longest(layers, remote_layers):
            images.append({'Name': name,
                           'Version': no_shaw(local),
                           'Id': no_shaw(local),
                           'Remote Version': no_shaw(remote),
                           'remote': remote,
                           'no_version' : True,
                           'Repo Tags': self.image,
            })

        self.print_verify(images, self.image, verbose=self.args.verbose)

    def validate_system_image_manifests(self,layers):
        """
        Validate a system image's layers against the the associated validation manifests
        created from those image layers on atomic pull.
        :param layers: list of the names of the layers to validate
        :return: None
        """
        for layer in layers:
            mismatches = self.syscontainers.validate_layer(layer)
            if len(mismatches) > 0:
                util.write_out("modifications in layer %s layer:\n" % layer)
                for m in mismatches:
                    util.write_out("file '%s' changed checksum from '%s' to '%s'" % (m["name"], m["old-checksum"], m["new-checksum"]))

    def validate_image_manifest(self):
        """
        Validates a docker image by mounting the image on a rootfs and validate that
        rootfs against the manifests that were created. Note that it won't be validated
        layer by layer.
        :param:
        :return: None
        """
        iid = self._is_image(self.image)
        manifestname = os.path.join(util.ATOMIC_VAR_LIB, "gomtree-manifests/%s.mtree" % iid)
        if not os.path.exists(manifestname):
            return
        tmpdir = tempfile.mkdtemp()
        m = Mount()
        m.args = []
        m.image = self.image
        m.mountpoint = tmpdir
        m.mount()
        r = util.validate_manifest(manifestname, img_rootfs=tmpdir, keywords="type,uid,gid,mode,size,sha256digest")
        m.unmount()
        if r.return_code != 0:
            util.write_err(r.stdout)
        shutil.rmtree(tmpdir)

    @staticmethod
    def get_gomtree_manifest(layer, root=os.path.join(util.ATOMIC_VAR_LIB, "gomtree-manifests")):
        manifestpath = os.path.join(root,"%s.mtree" % layer)
        if os.path.isfile(manifestpath):
            return manifestpath
        return None
Beispiel #46
0
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.debug:
            util.write_out(str(self.args))
        local_layers, remote_layers = self._verify()
        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(self.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")

    def verify_dbus(self):
        local_layers, remote_layers = self._verify()
        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):
        be, img_obj = self.backend_utils.get_backend_and_image_obj(self.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 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")

    def verify_system_image(self):
        manifest = self.syscontainers.get_manifest(self.image)
        name = json.loads(manifest).get('Name', self.image)
        if manifest:
            layers = SystemContainers.get_layers_from_manifest(manifest)
        else:
            layers = [self.image]
        if not getattr(self.args,"no_validate", False):
            self.validate_system_image_manifests(layers)

        if not manifest:
            return
        remote = True
        try:
            remote_manifest = self.syscontainers.get_manifest(self.image, remote=True)
            remote_layers = SystemContainers.get_layers_from_manifest(remote_manifest)
        except subprocess.CalledProcessError:
            remote_layers = []
            remote = False

        if hasattr(itertools, 'izip_longest'):
            zip_longest = getattr(itertools, 'izip_longest')
        else:
            zip_longest = getattr(itertools, 'zip_longest')

        images = []
        for local, remote in zip_longest(layers, remote_layers):
            images.append({'Name': name,
                           'Version': no_shaw(local),
                           'Id': no_shaw(local),
                           'Remote Version': no_shaw(remote),
                           'remote': remote,
                           'no_version' : True,
                           'Repo Tags': self.image,
            })

        self.print_verify(images, self.image, verbose=self.args.verbose)

    def validate_system_image_manifests(self,layers):
        """
        Validate a system image's layers against the the associated validation manifests
        created from those image layers on atomic pull.
        :param layers: list of the names of the layers to validate
        :return: None
        """
        for layer in layers:
            mismatches = self.syscontainers.validate_layer(layer)
            if len(mismatches) > 0:
                util.write_out("modifications in layer %s layer:\n" % layer)
                for m in mismatches:
                    util.write_out("file '%s' changed checksum from '%s' to '%s'" % (m["name"], m["old-checksum"], m["new-checksum"]))

    def validate_image_manifest(self):
        """
        Validates a docker image by mounting the image on a rootfs and validate that
        rootfs against the manifests that were created. Note that it won't be validated
        layer by layer.
        :param:
        :return: None
        """
        iid = self._is_image(self.image)
        manifestname = os.path.join(util.ATOMIC_VAR_LIB, "gomtree-manifests/%s.mtree" % iid)
        if not os.path.exists(manifestname):
            return
        tmpdir = tempfile.mkdtemp()
        m = Mount()
        m.args = []
        m.image = self.image
        m.mountpoint = tmpdir
        m.mount()
        r = util.validate_manifest(manifestname, img_rootfs=tmpdir, keywords="type,uid,gid,mode,size,sha256digest")
        m.unmount()
        if r.return_code != 0:
            util.write_err(r.stdout)
        shutil.rmtree(tmpdir)

    @staticmethod
    def get_gomtree_manifest(layer, root=os.path.join(util.ATOMIC_VAR_LIB, "gomtree-manifests")):
        manifestpath = os.path.join(root,"%s.mtree" % layer)
        if os.path.isfile(manifestpath):
            return manifestpath
        return None
Beispiel #47
0
 def __init__(self):
     super(Containers, self).__init__()
     self.beu = BackendUtils()
Beispiel #48
0
 def __init__(self):
     super(Verify, self).__init__()
     self.debug = False
     self.backend_utils = BackendUtils()