def remove(self, force=False, recursive=False): """Delete files or directories""" if not os.path.lexists(self.filename): pass elif self.filename.count("/") < 2: Msg().err("Error: delete pathname too short: ", self.filename) return False elif self.uid() != HostInfo.uid: Msg().err("Error: delete not owner: ", self.filename) return False elif (not force) and (not self._is_safe_prefix(self.filename)): Msg().err("Error: delete outside of directory tree: ", self.filename) return False elif os.path.isfile(self.filename) or os.path.islink(self.filename): try: os.remove(self.filename) except (IOError, OSError): Msg().err("Error: deleting file: ", self.filename) return False elif os.path.isdir(self.filename): if recursive: status = self._removedir() else: status = self.rmdir() if not status: Msg().err("Error: deleting directory: ", self.filename) return False if self.filename in dict(FileUtil.tmptrash): del FileUtil.tmptrash[self.filename] return True
def _check_exposed_ports(self): """TCP/UDP ports < 1024 in ExposedPorts JSON metadata The exposed ports format is ExposedPorts:{ "80/tcp":{}, } """ mapped_ports = self._get_portsmap() exposes_priv = False exposes_port = False for port in self.opt["portsexp"]: try: port_number = int(port.split("/")[0]) exposes_port = True except (ValueError, TypeError): pass else: if port_number < 1024: if port_number in list(mapped_ports.keys()): if mapped_ports[port_number] >= 1024: continue exposes_priv = True if exposes_priv and HostInfo.uid != 0: Msg().err("Error: this container exposes privileged TCP/IP ports") return False if exposes_port: Msg().out("Warning: this container exposes TCP/IP ports", l=Msg.WAR) return True
def load(self, imagefile, imagerepo=None): """Generic load of image tags to a file""" if not os.path.exists(imagefile) and imagefile != '-': Msg().err("Error: image file does not exist:", imagefile) return False tmp_imagedir = FileUtil("load").mktmp() try: os.makedirs(tmp_imagedir) except (IOError, OSError): return False if not self._untar_saved_container(imagefile, tmp_imagedir): Msg().err("Error: failed to extract container:", imagefile) FileUtil(tmp_imagedir).remove(recursive=True) return False imagetype = self._get_imagedir_type(tmp_imagedir) if imagetype == "Docker": repositories = DockerLocalFileAPI(self.localrepo).load( tmp_imagedir, imagerepo) elif imagetype == "OCI": repositories = OciLocalFileAPI(self.localrepo).load( tmp_imagedir, imagerepo) else: repositories = [] FileUtil(tmp_imagedir).remove(recursive=True) return repositories
def _check_executable(self): """Check if executable exists and has execute permissions""" if self.opt["entryp"] and is_genstr(self.opt["entryp"]): self.opt["entryp"] = self.opt["entryp"].strip().split(' ') if isinstance(self.opt["entryp"], list): if self.opt["cmd"]: # and cmd cmd_args = self.opt["cmd"] self.opt["cmd"] = self.opt["entryp"] self.opt["cmd"].extend(cmd_args) # cmd=args entryp else: self.opt["cmd"] = self.opt["entryp"] if not self.opt["cmd"]: self.opt["cmd"] = Config.conf['cmd'] Msg().err("Warning: no command assuming:", self.opt["cmd"], l=Msg.WAR) exec_name = self.opt["cmd"][0] # exec pathname without args if exec_name.startswith("./") or exec_name.startswith("../"): exec_name = self.opt["cwd"] + '/' + exec_name path = self.opt["env"].getenv("PATH") # DEBUG exec_name = FileUtil(exec_name).find_exec(path, self.container_root, self.opt["vol"], self.opt["cwd"]) if exec_name: return self.container_root + '/' + exec_name Msg().err("Error: command not found or has no execute bit set: ", self.opt["cmd"]) return ""
def verify_image(self): """Verify the structure of an image repository""" Msg().out("Info: loading structure", l=Msg.INF) structure = self._load_structure(self.cur_tagdir) if not structure: Msg().err("Error: load of image tag structure failed") return False Msg().out("Info: verifying layers", l=Msg.INF) status = True if "ancestry" in structure and "has_json_f" in structure: status = self._verify_image_v1(structure) elif "manifest" in structure: if "fsLayers" in structure["manifest"]: status = self._verify_image_v2_s1(structure) elif "layers" in structure["manifest"]: status = self._verify_image_v2_s2(structure) for layer_id in structure["repolayers"]: if "layer_f" not in structure["repolayers"][layer_id]: Msg().err("Error: layer file not found in structure", layer_id) status = False continue layer_status = self._verify_layer_file(structure, layer_id) if not layer_status: status = False continue Msg().out("Info: layer ok:", layer_id, l=Msg.INF) return status
def get_v2(self, imagerepo, tag): """Pull container with v2 API""" files = [] (hdr_data, manifest) = self.get_v2_image_manifest(imagerepo, tag) status = self.curl.get_status_code(hdr_data["X-ND-HTTPSTATUS"]) if status == 401: Msg().err("Error: manifest not found or not authorized") return [] if status != 200: Msg().err("Error: pulling manifest:") return [] try: if not (self.localrepo.setup_tag(tag) and self.localrepo.set_version("v2")): Msg().err("Error: setting localrepo v2 tag and version") return [] self.localrepo.save_json("manifest", manifest) Msg().out("Info: v2 layers: %s" % (imagerepo), l=Msg.DBG) if "fsLayers" in manifest: files = self.get_v2_layers_all(imagerepo, manifest["fsLayers"]) elif "layers" in manifest: if "config" in manifest: manifest["layers"].append(manifest["config"]) files = self.get_v2_layers_all(imagerepo, manifest["layers"]) else: Msg().err("Error: layers section missing in manifest") except (KeyError, AttributeError, IndexError, ValueError, TypeError): pass return files
def test_04_err(self, mock_stderr): """Test04 Msg.err().""" msg = Msg(Msg.ERR) msg.err("111", "222", "333", 444, ('555')) self.assertEqual("111 222 333 444 555\n", mock_stderr.getvalue()) sys.stdout = STDOUT sys.stderr = STDERR
def clone(self): """Clone a container by creating a complete copy """ source_container_dir = self.localrepo.cd_container(self.container_id) if not source_container_dir: Msg().err("Error: source container not found:", self.container_id) return False dest_container_id = Unique().uuid(os.path.basename(self.imagerepo)) dest_container_dir = self.localrepo.setup_container( "CLONING", "inprogress", dest_container_id) if not dest_container_dir: Msg().err("Error: create destination container: setting up") return False status = FileUtil(source_container_dir).copydir(dest_container_dir) if not status: Msg().err("Error: creating container:", dest_container_id) return False if not self._chk_container_root(dest_container_id): Msg().out("Warning: check container content:", dest_container_id, l=Msg.WAR) return dest_container_id
def _load_image_step2(self, structure, imagerepo, tag): """Load a container image into a repository mimic docker load""" imagetag = imagerepo + ':' + tag (json_config_file, layers) = \ self._get_from_manifest(structure, imagetag) if json_config_file: layer_id = json_config_file.replace(".json", "") json_file = structure["repoconfigs"][json_config_file]["json_f"] self._move_layer_to_v1repo(json_file, layer_id, "container.json") top_layer_id = self._find_top_layer_id(structure) layers = self._sorted_layers(structure, top_layer_id) for layer_id in layers: Msg().out("Info: adding layer:", layer_id, l=Msg.INF) if str(structure["repolayers"][layer_id]["VERSION"]) != "1.0": Msg().err("Error: layer version unknown") return [] for layer_item in ("json_f", "layer_f"): filename = str(structure["repolayers"][layer_id][layer_item]) if not self._move_layer_to_v1repo(filename, layer_id): Msg().err("Error: copying %s file %s" % (layer_item[:-2], filename), l=Msg.VER) return [] self.localrepo.save_json("ancestry", layers) if self._imagerepo: imagetag = self._imagerepo + ':' + tag return [imagetag]
def save(self, imagetag_list, imagefile): """Save a set of image tags to a file similarly to docker save """ tmp_imagedir = FileUtil("save").mktmp() try: os.makedirs(tmp_imagedir) except (IOError, OSError): return False structure = dict() structure["manifest"] = [] structure["repositories"] = dict() status = False for (imagerepo, tag) in imagetag_list: status = self._save_image(imagerepo, tag, structure, tmp_imagedir) if not status: Msg().err("Error: save image failed:", imagerepo + ':' + tag) break if status: self.localrepo.save_json(tmp_imagedir + "/manifest.json", structure["manifest"]) self.localrepo.save_json(tmp_imagedir + "/repositories", structure["repositories"]) if not FileUtil(tmp_imagedir).tar(imagefile): Msg().err("Error: save image failed in writing tar", imagefile) status = False else: Msg().err("Error: no images specified") FileUtil(tmp_imagedir).remove(recursive=True) return status
def _add_device_spec(self, dev_path, mode="rwm"): """Add device to the configuration""" if not (os.path.exists(dev_path) and dev_path.startswith("/dev/")): Msg().err("Error: device not found", dev_path) return False dev_stat = os.stat(dev_path) if stat.S_ISBLK(dev_stat.st_mode): dev_type = 'b' elif stat.S_ISCHR(dev_stat.st_mode): dev_type = 'c' else: Msg().err("Error: not a device", dev_path) return False filemode = 0 if 'r' in mode.lower(): filemode += 0o444 if 'w' in mode.lower(): filemode += 0o222 if not filemode: filemode = 0o666 if "devices" not in self._container_specjson["linux"]: self._container_specjson["linux"]["devices"] = [] device = { "path": dev_path, "type": dev_type, "major": os.major(dev_stat.st_dev), "minor": os.minor(dev_stat.st_dev), "fileMode": filemode, "uid": HostInfo.uid, "gid": HostInfo.gid, } self._container_specjson["linux"]["devices"].append(device) return True
def _setup_container_user_noroot(self, user): """ Setup user for engines without root support. Equivalent to _setup_container_user() for engines without root support. """ host_auth = NixAuthentication() (passwd, group) = self._select_auth_files() container_auth = NixAuthentication(passwd, group) if not user: user = HostInfo().username() (valid_user, user_id) = self._user_from_str(user, host_auth, container_auth) if not valid_user: Msg().err("Error: invalid syntax for user", user) return False if self.opt["user"] == "root": self.opt["user"] = HostInfo().username() self.opt["uid"] = str(HostInfo.uid) self.opt["gid"] = str(HostInfo.gid) if (self._is_mountpoint("/etc/passwd") or self._is_mountpoint("/etc/group")): self.opt["hostauth"] = self.opt["containerauth"] = False return True if self.opt["user"]: self.opt["uid"] = str(HostInfo.uid) self.opt["gid"] = str(HostInfo.gid) else: if self.opt["hostauth"] or self.opt["containerauth"]: Msg().err("Error: user not found on host") return False self.opt["user"] = user_id["user"] if "user" in user_id else user self.opt["uid"] = user_id["uid"] if "uid" in user_id else "" self.opt["gid"] = user_id["gid"] if "gid" in user_id else "" self._create_user(container_auth, host_auth) return True
def create_fromlayer(self, imagerepo, tag, layer_file, container_json): """Create a container from a layer file exported by Docker. """ self.imagerepo = imagerepo self.tag = tag if not self.container_id: self.container_id = Unique().uuid(os.path.basename(self.imagerepo)) if not container_json: Msg().err("Error: create container: getting json") return False container_dir = self.localrepo.setup_container(self.imagerepo, self.tag, self.container_id) if not container_dir: Msg().err("Error: create container: setting up") return False fjson = container_dir + "/container.json" self.localrepo.save_json(fjson, container_json) status = self._untar_layers([ layer_file, ], container_dir + "/ROOT") if not status: Msg().err("Error: creating container:", self.container_id) elif not self._chk_container_root(): Msg().out("Warning: check container content:", self.container_id, l=Msg.WAR) return self.container_id
def set_mode(self, force=False): """Set nvidia mode""" if not self.container_dir: Msg().err("Error: nvidia set mode container dir not found") return False nvi_host_dir_list = self._find_host_dir() nvi_cont_dir = self._find_cont_dir() if not nvi_host_dir_list: Msg().err("Error: host nvidia libraries not found") return False if not nvi_cont_dir: Msg().err("Error: destination dir for nvidia libs not found") return False if (not force) and self._installation_exists(nvi_host_dir_list, nvi_cont_dir): Msg().err("Error: nvidia installation already exists" ", use --force to overwrite") return False for nvi_host_dir in nvi_host_dir_list: lib_list = self._get_nvidia_libs(nvi_host_dir) self._copy_files(nvi_host_dir, nvi_cont_dir, lib_list, force) self._copy_files('/etc', '/etc', Config.conf['nvi_etc_list'], force) self._copy_files('/usr/bin', '/usr/bin', Config.conf['nvi_bin_list'], force) FileUtil(self._container_nvidia_set).putdata("", 'w') Msg().out("Info: nvidia mode set") return True
def test_03_out(self, mock_stdout): """Test03 Msg.out().""" msg = Msg(Msg.MSG) msg.out("111", "222", "333", 444, ('555')) self.assertEqual("111 222 333 444 555\n", mock_stdout.getvalue()) sys.stdout = STDOUT sys.stderr = STDERR
def _run_invalid_options(self): """check -p --publish -P --publish-all --net-coop""" if self.opt["portsmap"]: Msg().out("Warning: this execution mode does not support " "-p --publish", l=Msg.WAR) if self.opt["netcoop"]: Msg().out("Warning: this execution mode does not support " "-P --netcoop --publish-all", l=Msg.WAR)
def run(self, container_id): """Execute a Docker container using Fakechroot. This is the main method invoked to run the a container with Fakechroot. * argument: container_id or name * options: many via self.opt see the help """ # warning root execution not supported self._uid_check() # setup execution exec_path = self._run_init(container_id) if not exec_path: return 2 self._run_invalid_options() # execution mode and get patcher xmode = self.exec_mode.get_mode() self._elfpatcher = ElfPatcher(self.localrepo, self.container_id) # verify if container pathnames are correct for this mode if not self._elfpatcher.check_container_path(): Msg().out("Warning: container path mismatch, use setup to convert", l=Msg.WAR) # set basic environment variables self._run_env_set() self._fakechroot_env_set() # if not --hostenv clean the environment self._run_env_cleanup_list() # build the actual command cmd_l = self._set_cpu_affinity() cmd_l.extend([ "env", "-i", ]) cmd_l.extend(self.opt["env"].list()) if xmode in ("F1", "F2"): container_loader = self._elfpatcher.get_container_loader() if container_loader: cmd_l.append(container_loader) cmd_l.extend(self._run_add_script_support(exec_path)) cmd_l.extend(self.opt["cmd"]) Msg().out("CMD =", cmd_l, l=Msg.VER) # execute self._run_banner(self.opt["cmd"][0], '#') cwd = FileUtil(self.container_root).cont2host(self.opt["cwd"], self.opt["vol"]) status = subprocess.call(cmd_l, shell=False, close_fds=True, cwd=cwd) return status
def setup(self): """Prepare container for FileBind""" if not os.path.isdir(self.container_orig_dir): if not FileUtil(self.container_orig_dir).mkdir(): Msg().err("Error: creating dir:", self.container_orig_dir) return False if not os.path.isdir(self.container_bind_dir): if not FileUtil(self.container_bind_dir).mkdir(): Msg().err("Error: creating dir:", self.container_bind_dir) return False return True
def _untar_layers(self, tarfiles, destdir): """Untar all container layers. Each layer is extracted and permissions are changed to avoid file permission issues when extracting the next layer. """ if not (tarfiles and destdir): return False status = True gid = str(HostInfo.gid) optional_flags = [ "--wildcards", "--delay-directory-restore", ] for option in optional_flags: if not HostInfo().cmd_has_option("tar", option): optional_flags.remove(option) for tarf in tarfiles: if tarf != '-': self._apply_whiteouts(tarf, destdir) verbose = '' if Msg.level >= Msg.VER: verbose = 'v' Msg().out("Info: extracting:", tarf, l=Msg.INF) cmd = [ "tar", "-C", destdir, "-x" + verbose, "--one-file-system", "--no-same-owner", "--overwrite", "--exclude=dev/*", "--exclude=etc/udev/devices/*", "--no-same-permissions", r"--exclude=.wh.*", ] + optional_flags + ["-f", tarf] if subprocess.call(cmd, stderr=Msg.chlderr, close_fds=True): Msg().err("Error: while extracting image layer") status = False cmd = [ "find", destdir, "(", "-type", "d", "!", "-perm", "-u=x", "-exec", "chmod", "u+x", "{}", ";", ")", ",", "(", "!", "-perm", "-u=w", "-exec", "chmod", "u+w", "{}", ";", ")", ",", "(", "!", "-perm", "-u=r", "-exec", "chmod", "u+r", "{}", ";", ")", ",", "(", "!", "-gid", gid, "-exec", "chgrp", gid, "{}", ";", ")", ",", "(", "-name", ".wh.*", "-exec", "rm", "-f", "--preserve-root", "{}", ";", ")" ] if subprocess.call(cmd, stderr=Msg.chlderr, close_fds=True): status = False Msg().err("Error: while modifying attributes of image layer") return status
def restore_ld(self): """Restore ld.so""" elf_loader = self.get_container_loader() futil_ldso = FileUtil(self._container_ld_so_orig) if futil_ldso.size() <= 0: Msg().err("Error: original loader not found or empty") return False if not futil_ldso.copyto(elf_loader): Msg().err("Error: in loader copy or file locked by other process") return False return True
def get_installinfo(self): """Get json containing installation info""" Msg().out("Info: searching for messages:", l=Msg.VER) for url in self._get_mirrors(self._installinfo): infofile = self._get_file(url) try: with open(infofile, 'r') as filep: self._install_json = json.load(filep) for msg in self._install_json["messages"]: Msg().out("Info:", msg) except (KeyError, AttributeError, ValueError, OSError, IOError): Msg().out("Info: no messages:", infofile, url, l=Msg.VER) return self._install_json
def _copy_files(self, host_src_dir, cont_dst_dir, files_list, force=False): """copy or link file to destination creating directories as needed""" Msg().out("Source (host) dir ", host_src_dir, l=Msg.DBG) Msg().out("Destination (container) dir ", cont_dst_dir, l=Msg.DBG) for fname in files_list: srcname = host_src_dir + '/' + fname dstname = self.container_root + '/' + cont_dst_dir + '/' + fname if os.path.isfile(dstname) or os.path.islink(dstname): if force: try: os.remove(dstname) except OSError: Msg().err("Error: deleting nvidia file", dstname) else: Msg().out("Info: nvidia file already exists", dstname, ", use --force to overwrite", l=Msg.INF) return False srcdir = os.path.dirname(srcname) dstdir = os.path.dirname(dstname) if not os.path.isdir(dstdir): try: os.makedirs(dstdir) os.chmod( dstdir, stat.S_IMODE(os.stat(srcdir).st_mode) | stat.S_IRWXU) except OSError: Msg().err("Error: creating nvidia dir", dstdir) if os.path.islink(srcname): linkto = os.readlink(srcname) os.symlink(linkto, dstname) Msg().out("Info: is link", srcname, "to", dstname, l=Msg.DBG) elif os.path.isfile(srcname): shutil.copy2(srcname, dstname) Msg().out("Info: is file", srcname, "to", dstname, l=Msg.DBG) try: mask = stat.S_IMODE(os.stat(srcname).st_mode) | \ stat.S_IWUSR | stat.S_IRUSR if os.access(srcname, os.X_OK): mask = mask | stat.S_IXUSR os.chmod(dstname, mask) except (IOError, OSError) as error: Msg().err("Error: change mask of nvidia file", error) else: Msg().err("Warn: nvidia file in config not found", srcname) Msg().out("Info: nvidia copied", srcname, "to", dstname, l=Msg.DBG) return True
def export_tofile(self, clone_file): """Export a container creating a tar file of the rootfs """ cont_dir = self.localrepo.cd_container(self.container_id) if not cont_dir: Msg().err("Error: container not found:", self.container_id) return False status = FileUtil(cont_dir + "/ROOT").tar(clone_file) if not status: Msg().err("Error: exporting container file system:", self.container_id) return self.container_id
def clone_tofile(self, clone_file): """Create a cloned container tar file containing both the rootfs and all udocker control files. This is udocker specific. """ container_dir = self.localrepo.cd_container(self.container_id) if not container_dir: Msg().err("Error: container not found:", self.container_id) return False status = FileUtil(container_dir).tar(clone_file) if not status: Msg().err("Error: export container as clone:", self.container_id) return self.container_id
def get_v1(self, imagerepo, tag): """Pull container with v1 API""" Msg().out("Info: v1 image id: %s" % (imagerepo), l=Msg.DBG) (hdr_data, images_array) = self.get_v1_repo(imagerepo) status = self.curl.get_status_code(hdr_data["X-ND-HTTPSTATUS"]) if status == 401 or not images_array: Msg().err("Error: image not found or not authorized") return [] try: endpoint = "http://" + hdr_data["x-docker-endpoints"] except KeyError: endpoint = self.index_url (tags_array) = self.get_v1_image_tags(endpoint, imagerepo) image_id = self._get_v1_id_from_tags(tags_array, tag) if not image_id: Msg().err("Error: image tag not found") return [] if len(image_id) <= 8: image_id = self._get_v1_id_from_images(images_array, image_id) if not image_id: Msg().err("Error: image id not found") return [] if not (self.localrepo.setup_tag(tag) and self.localrepo.set_version("v1")): Msg().err("Error: setting localrepo v1 tag and version") return [] Msg().out("Info: v1 ancestry", image_id, l=Msg.DBG) (dummy, ancestry) = self.get_v1_image_ancestry(endpoint, image_id) if not ancestry: Msg().err("Error: ancestry not found") return [] self.localrepo.save_json("ancestry", ancestry) Msg().out("Info: v1 layers", image_id, l=Msg.DBG) files = self.get_v1_layers_all(endpoint, ancestry) return files
def _select_implementation(self): """Select which implementation to use""" if GetURLpyCurl().is_available() and not self._curl_exec: self._geturl = GetURLpyCurl() self.cache_support = True Msg().out("Info: using pycurl", l=Msg.DBG) elif GetURLexeCurl().is_available(): self._geturl = GetURLexeCurl() Msg().out("Info: using curl executable", self._geturl._curl_exec, l=Msg.DBG) else: Msg().err("Error: need curl or pycurl to perform downloads") raise NameError('need curl or pycurl')
def _download(self, url): """Download a file """ download_file = FileUtil("udockertools").mktmp() if Msg.level <= Msg.DEF: Msg().setlevel(Msg.NIL) (hdr, dummy) = self.curl.get(url, ofile=download_file, follow=True) if Msg.level == Msg.NIL: Msg().setlevel() try: if "200" in hdr.data["X-ND-HTTPSTATUS"]: return download_file except (KeyError, TypeError, AttributeError): pass FileUtil(download_file).remove() return ""
def unshare(self, flags): """Python implementation of unshare""" try: _unshare = ctypes.CDLL("libc.so.6").unshare except OSError: Msg().err("Error: in unshare: mapping libc") return False _unshare.restype = ctypes.c_int _unshare.argtypes = (ctypes.c_int, ) if _unshare(flags) == -1: Msg().err("Error: in unshare:", os.strerror(-1)) return False return True
def _get_url(self, *args, **kwargs): """Encapsulates the call to GetURL.get() so that authentication for v1 and v2 repositories can be treated differently. Example: _get_url(url, ctimeout=5, timeout=5, header=[]): """ url = str(args[0]) if "RETRY" not in kwargs: kwargs["RETRY"] = 3 if "FOLLOW" not in kwargs: kwargs["FOLLOW"] = 3 kwargs["RETRY"] -= 1 (hdr, buf) = self.curl.get(*args, **kwargs) Msg().out("Info: header: %s" % (hdr.data), l=Msg.DBG) Msg().out("Info: buffer: %s" % (buf.getvalue()), l=Msg.DBG) status_code = self.curl.get_status_code(hdr.data["X-ND-HTTPSTATUS"]) if status_code == 200: return (hdr, buf) if not kwargs["RETRY"]: hdr.data["X-ND-CURLSTATUS"] = 13 # Permission denied return (hdr, buf) auth_kwargs = kwargs.copy() if "location" not in hdr.data: kwargs["FOLLOW"] = 3 if "location" in hdr.data and hdr.data['location']: if not kwargs["FOLLOW"]: hdr.data["X-ND-CURLSTATUS"] = 13 return (hdr, buf) kwargs["FOLLOW"] -= 1 args = [hdr.data['location']] if "header" in auth_kwargs: del auth_kwargs["header"] elif status_code == 401: if "www-authenticate" in hdr.data: www_authenticate = hdr.data["www-authenticate"] if not "realm" in www_authenticate: return (hdr, buf) if 'error="insufficient_scope"' in www_authenticate: return (hdr, buf) auth_header = "" if "/v2/" in url: auth_header = self._get_v2_auth(www_authenticate, kwargs["RETRY"]) if "/v1/" in url: auth_header = self._get_v1_auth(www_authenticate) auth_kwargs.update({"header": [auth_header]}) (hdr, buf) = self._get_url(*args, **auth_kwargs) return (hdr, buf)
def get_tags(self, imagerepo): """List tags from a v2 or v1 repositories""" Msg().out("Info: get tags", imagerepo, l=Msg.DBG) (dummy, remoterepo) = self._parse_imagerepo(imagerepo) if self.is_v2(): return self.get_v2_image_tags(remoterepo, True) # try v2 return self.get_v1_image_tags(remoterepo, True) # try v1