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
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))
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)
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)
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
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)
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)
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)
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)
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)
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)
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
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)
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)
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)
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)
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