def get_config(self, spec="Entrypoint", delim=None, old_version=False): '''get_config returns a particular spec (default is Entrypoint) from a VERSION 1 manifest obtained with get_manifest. :param manifest: the manifest obtained from get_manifest :param spec: the key of the spec to return, default is "Entrypoint" :param delim: Given a list, the delim to use to join the entries. Default is newline ''' manifest = self.get_manifest(old_version=old_version) cmd = None # Version 1 of the manifest has more detailed metadata if old_version: if "history" in manifest: for entry in manifest['history']: if 'v1Compatibility' in entry: entry = json.loads(entry['v1Compatibility']) if "config" in entry: if spec in entry["config"]: cmd = entry["config"][spec] # Standard is to include commands like ['/bin/sh'] if isinstance(cmd, list): if delim is None: delim = "\n" cmd = delim.join(cmd) bot.verbose("Found Docker config (%s) %s" % (spec, cmd)) else: if "config" in manifest: if spec in manifest['config']: cmd = manifest['config'][spec] return cmd
def extract_metadata(manifest, labelfile=None, prefix=None): '''extract_metadata will write a file of metadata from shub :param manifest: the manifest to use ''' if prefix is None: prefix = "" prefix = prefix.upper() source = 'Hub' if 'frozen' in manifest: source = 'Registry' metadata = manifest.copy() remove_fields = ['files', 'spec', 'metrics'] for remove_field in remove_fields: if remove_field in metadata: del metadata[remove_field] if labelfile is not None: for key, value in metadata.items(): key = "%s%s" % (prefix, key) value = ADD(key=key, value=value, jsonfile=labelfile, force=True) bot.verbose("Saving Singularity %s metadata to %s" % (source, labelfile)) return metadata
def get_tags(self, return_response=False): '''get_tags will return the tags for a repo using the Docker Version 2.0 Registry API ''' registry = self.registry if registry is None: registry = self.api_base registry = add_http(registry) # make sure we have a complete url base = "%s/%s/%s/tags/list" % (registry, self.api_version, self.repo_name) bot.verbose("Obtaining tags: %s" % base) # We use get_tags for a testing endpoint in update_token response = self.get(base, return_response=return_response) if return_response: return response try: response = json.loads(response) return response['tags'] except Exception: bot.error("Error obtaining tags: %s" % base) sys.exit(1)
def get_tags(self,return_response=False): '''get_tags will return the tags for a repo using the Docker Version 2.0 Registry API :param namespace: the namespace (eg, "library") :param repo_name: the name for the repo (eg, "ubuntu") :param registry: the docker registry to use (default will use index.docker.io) :param auth: authorization header (default None) ''' registry = self.registry if registry == None: registry = self.api_base registry = add_http(registry) # make sure we have a complete url base = "%s/%s/%s/%s/tags/list" %(registry,self.api_version,self.namespace,self.repo_name) bot.verbose("Obtaining tags: %s" %base) # We use get_tags for a testing endpoint in update_token response = self.get(base, return_response=return_response) if return_response: return response try: response = json.loads(response) return response['tags'] except: bot.error("Error obtaining tags: %s" %base) sys.exit(1)
def get_manifest(self,old_version=False): '''get_manifest should return an image manifest for a particular repo and tag. The image details are extracted when the client is generated. :param old_version: return version 1 (for cmd/entrypoint), default False ''' registry = self.registry if registry == None: registry = self.api_base registry = add_http(registry) # make sure we have a complete url base = "%s/%s/%s/%s/manifests" %(registry,self.api_version,self.namespace,self.repo_name) if self.version is not None: base = "%s/%s" %(base,self.version) else: base = "%s/%s" %(base,self.repo_tag) bot.verbose("Obtaining manifest: %s" %base) headers = self.headers if old_version == True: headers['Accept'] = 'application/json' response = self.get(base,headers=self.headers) try: response = json.loads(response) except: # If the call fails, give the user a list of acceptable tags tags = self.get_tags() print("\n".join(tags)) repo_uri = "%s/%s:%s" %(self.namespace,self.repo_name,self.repo_tag) bot.error("Error getting manifest for %s, exiting." %repo_uri) sys.exit(1) return response
def extract_metadata(manifest, labelfile=None, prefix=None): '''extract_metadata will write a file of metadata from shub :param manifest: the manifest to use ''' if prefix is None: prefix = "" prefix = prefix.upper() source = 'Hub' if 'frozen' in manifest: source = 'Registry' metadata = manifest.copy() remove_fields = ['files', 'spec', 'metrics'] for remove_field in remove_fields: if remove_field in metadata: del metadata[remove_field] if labelfile is not None: for key, value in metadata.items(): key = "%s%s" % (prefix, key) value = ADD(key=key, value=value, jsonfile=labelfile, force=True) bot.verbose("Saving Singularity %s metadata to %s" % (source, labelfile)) return metadata
def get_tags(self, return_response=False): '''get_tags will return the tags for a repo using the Docker Version 2.0 Registry API ''' registry = self.registry if registry is None: registry = self.api_base registry = add_http(registry) # make sure we have a complete url base = "%s/%s/%s/tags/list" % (registry, self.api_version, self.repo_name) bot.verbose("Obtaining tags: %s" % base) # We use get_tags for a testing endpoint in update_token response = self.get(base, return_response=return_response) if return_response: return response try: response = json.loads(response) return response['tags'] except Exception: bot.error("Error obtaining tags: %s" % base) sys.exit(1)
def get_tags(self, return_response=False): '''get_tags will return the tags for a repo using the Docker Version 2.0 Registry API :param namespace: the namespace (eg, "library") :param repo_name: the name for the repo (eg, "ubuntu") :param registry: the docker registry to use (default will use index.docker.io) :param auth: authorization header (default None) ''' registry = self.registry if registry == None: registry = self.api_base registry = add_http(registry) # make sure we have a complete url base = "%s/%s/%s/%s/tags/list" % (registry, self.api_version, self.namespace, self.repo_name) bot.verbose("Obtaining tags: %s" % base) # We use get_tags for a testing endpoint in update_token response = self.get(base, return_response=return_response) if return_response: return response try: response = json.loads(response) return response['tags'] except: bot.error("Error obtaining tags: %s" % base) sys.exit(1)
def get_config(self,spec="Entrypoint",delim=None,old_version=False): '''get_config returns a particular spec (default is Entrypoint) from a VERSION 1 manifest obtained with get_manifest. :param manifest: the manifest obtained from get_manifest :param spec: the key of the spec to return, default is "Entrypoint" :param delim: Given a list, the delim to use to join the entries. Default is newline ''' manifest = self.get_manifest(old_version=old_version) cmd = None # Version 1 of the manifest has more detailed metadata if old_version: if "history" in manifest: for entry in manifest['history']: if 'v1Compatibility' in entry: entry = json.loads(entry['v1Compatibility']) if "config" in entry: if spec in entry["config"]: cmd = entry["config"][spec] # Standard is to include commands like ['/bin/sh'] if isinstance(cmd,list): if delim is None: delim = "\n" cmd = delim.join(cmd) bot.verbose("Found Docker config (%s) %s" %(spec,cmd)) else: if "config" in manifest: if spec in manifest['config']: cmd = manifest['config'][spec] return cmd
def get_layer(self, image_id, download_folder=None, change_perms=False, return_tmp=False): '''get_layer will download an image layer (.tar.gz) to a specified download folder. :param download_folder: if specified, download to folder. Otherwise return response with raw data :param change_perms: change permissions additionally (default False to support multiprocessing) :param return_tmp: If true, return the temporary file name (and don't rename to the file's final name). Default is False, should be True for multiprocessing that requires extra permission changes ''' registry = self.registry if registry is None: registry = self.api_base # make sure we have a complete url registry = add_http(registry) # The <name> variable is the namespace/repo_name base = "%s/%s/%s/%s/blobs/%s" % (registry, self.api_version, self.namespace, self.repo_name, image_id) bot.verbose("Downloading layers from %s" % base) if download_folder is None: download_folder = tempfile.mkdtemp() download_folder = "%s/%s.tar.gz" % (download_folder, image_id) # Update user what we are doing bot.debug("Downloading layer %s" % image_id) # Step 1: Download the layer atomically file_name = "%s.%s" % (download_folder, next(tempfile._get_candidate_names())) tar_download = self.download_atomically(url=base, file_name=file_name) bot.debug('Download of raw file (pre permissions fix) is %s' % tar_download) # Step 2: Fix Permissions? if change_perms: tar_download = change_tar_permissions(tar_download) if return_tmp is True: return tar_download try: os.rename(tar_download, download_folder) except Exception: msg = "Cannot untar layer %s," % tar_download msg += " was there a problem with download?" bot.error(msg) sys.exit(1) return download_folder
def change_tar_permissions(tar_file, file_permission=None, folder_permission=None): '''change_tar_permissions changes a permission if any member in a tarfile file does not have it :param file_path the path to the file :param file_permission: stat permission to use for files :param folder_permission: stat permission to use for folders ''' tar = tarfile.open(tar_file, "r:gz") # Owner read, write (o+rw) if file_permission is None: file_permission = stat.S_IRUSR | stat.S_IWUSR # Owner read, write execute (o+rwx) if folder_permission is None: folder_permission = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR # Add owner write permission to all, not symlinks members = tar.getmembers() if len(members) > 0: bot.verbose("Fixing permission for %s" % tar_file) # Add all content objects to file fd, tmp_tar = tempfile.mkstemp(prefix=("%s.fixperm." % tar_file)) os.close(fd) fixed_tar = tarfile.open(tmp_tar, "w:gz") for member in members: # add o+rwx for directories if member.isdir() and not member.issym(): member.mode = folder_permission | member.mode extracted = tar.extractfile(member) fixed_tar.addfile(member, extracted) # add o+rw for plain files elif member.isfile() and not member.issym(): member.mode = file_permission | member.mode extracted = tar.extractfile(member) fixed_tar.addfile(member, extracted) else: fixed_tar.addfile(member) fixed_tar.close() tar.close() # Rename the fixed tar to be the old name os.rename(tmp_tar, tar_file) else: tar.close() bot.warning("Tar file %s is empty, skipping." % tar_file) return tar_file
def change_tar_permissions(tar_file, file_permission=None, folder_permission=None): '''change_tar_permissions changes a permission if any member in a tarfile file does not have it :param file_path the path to the file :param file_permission: stat permission to use for files :param folder_permission: stat permission to use for folders ''' tar = tarfile.open(tar_file, "r:gz") # Owner read, write (o+rw) if file_permission is None: file_permission = stat.S_IRUSR | stat.S_IWUSR # Owner read, write execute (o+rwx) if folder_permission is None: folder_permission = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR # Add owner write permission to all, not symlinks members = tar.getmembers() if len(members) > 0: bot.verbose("Fixing permission for %s" % tar_file) # Add all content objects to file fd, tmp_tar = tempfile.mkstemp(prefix=("%s.fixperm." % tar_file)) os.close(fd) fixed_tar = tarfile.open(tmp_tar, "w:gz") for member in members: # add o+rwx for directories if member.isdir() and not member.issym(): member.mode = folder_permission | member.mode extracted = tar.extractfile(member) fixed_tar.addfile(member, extracted) # add o+rw for plain files elif member.isfile() and not member.issym(): member.mode = file_permission | member.mode extracted = tar.extractfile(member) fixed_tar.addfile(member, extracted) else: fixed_tar.addfile(member) fixed_tar.close() tar.close() # Rename the fixed tar to be the old name os.rename(tmp_tar, tar_file) else: tar.close() bot.warning("Tar file %s is empty, skipping." % tar_file) return tar_file
def SIZE(image,auth=None,contentfile=None): '''size is intended to be run before an import, to return to the contentfile a list of sizes (one per layer) corresponding with the layers that will be downloaded for image ''' bot.debug("Starting Docker SIZE, will get size from manifest") bot.verbose("Docker image: %s" %image) client = DockerApiConnection(image=image,auth=auth) size = client.get_size() if contentfile is not None: write_file(contentfile,str(size),mode="w") return size
def get_manifest(self, old_version=False, version=None): '''get_manifest should return an image manifest for a particular repo and tag. The image details are extracted when the client is generated. :param old_version: return version 1 (for cmd/entrypoint), default False ''' registry = self.registry if registry is None: registry = self.api_base # make sure we have a complete url registry = add_http(registry) base = "%s/%s/%s/%s/manifests" % (registry, self.api_version, self.namespace, self.repo_name) # First priority given to calling function if version is not None: base = "%s/%s" % (base, version) elif self.version is not None: base = "%s/%s" % (base, self.version) else: base = "%s/%s" % (base, self.repo_tag) bot.verbose("Obtaining manifest: %s" % base) headers = self.headers.copy() if old_version is True: headers['Accept'] = 'application/json' response = self.get(base, headers=headers) try: response = json.loads(response) except Exception: # If the call fails, give the user a list of acceptable tags tags = self.get_tags() print("\n".join(tags)) repo_uri = "%s/%s:%s" % (self.namespace, self.repo_name, self.repo_tag) bot.error("Error getting manifest for %s, exiting." % repo_uri) sys.exit(1) # If we have errors, don't continue return self.check_errors(response)
def get_layer(self,image_id,download_folder=None,change_perms=False,return_tmp=False): '''get_layer will download an image layer (.tar.gz) to a specified download folder. :param download_folder: if specified, download to folder. Otherwise return response with raw data (not recommended) :param change_perms: change permissions additionally (default False to support multiprocessing) :param return_tmp: If true, return the temporary file name (and don't rename to the file's final name). Default is False, should be True for multiprocessing that requires extra permission changes ''' registry = self.registry if registry == None: registry = self.api_base registry = add_http(registry) # make sure we have a complete url # The <name> variable is the namespace/repo_name base = "%s/%s/%s/%s/blobs/%s" %(registry,self.api_version,self.namespace,self.repo_name,image_id) bot.verbose("Downloading layers from %s" %base) if download_folder is None: download_folder = tempfile.mkdtemp() download_folder = "%s/%s.tar.gz" %(download_folder,image_id) # Update user what we are doing bot.debug("Downloading layer %s" %image_id) # Step 1: Download the layer atomically file_name = "%s.%s" %(download_folder,next(tempfile._get_candidate_names())) tar_download = self.download_atomically(url=base, file_name=file_name) bot.debug('Download of raw file (pre permissions fix) is %s' %tar_download) # Step 2: Fix Permissions? if change_perms: tar_download = change_tar_permissions(tar_download) if return_tmp is True: return tar_download try: os.rename(tar_download,download_folder) except: bot.error("Cannot untar layer %s, was there a problem with download?" %tar_download) sys.exit(1) return download_folder
def parse_image_uri(image, uri=None, quiet=False, default_registry=None, default_namespace=None, default_tag=None): '''parse_image_uri will parse a docker or shub uri and return a json structure with a registry, repo name, tag, namespace and version. Tag and version are optional, namespace is optional for docker:// uris. URIs are of this general form: myuri://[registry.com:port/][namespace/nested/]repo[:tag][@version] (parts in [] are optional) Parsing rules are slightly different if the uri is a docker:// uri: - registry must include a :port or a . in its name, else will be parsed as a namespace. For non-docker uris, instead, if there are three or more parts separated by /, the first one is taken to be the registry - namespace can be empty if a registry is specified, else default namespace will be used (e.g. docker://registry.com/repo:tag). For non-docker uris, namespace cannot be empty and default will be used :param image: the string provided on command line for the image name, eg: ubuntu:latest or docker://local.registry/busybox@12345 :param uri: the uri type (eg, docker://), default autodetects ::note uri is maintained as variable so we have some control over allowed :param quiet: If True, don't show verbose messages with the parsed values :default_registry: Which registry to use if image doesn't contain one. if None, use defaults.REGISTRY :default_namespace: Which namespace to use if image doesn't contain one. if None, use defaults.NAMESPACE. Also, check out the note above about docker:// and empty namespaces. :default_tag: Which tag to use if image doesn't contain one. if None, use defaults.REPO_TAG :returns parsed: a json structure with repo_name, repo_tag, and namespace ''' # Default to most common use case, Docker if default_registry is None: default_registry = DOCKER_API_BASE if default_namespace is None: default_namespace = NAMESPACE if default_tag is None: default_tag = TAG # if user gave custom registry / namespace, make them the default if CUSTOM_NAMESPACE is not None: default_namespace = CUSTOM_NAMESPACE if CUSTOM_REGISTRY is not None: default_registry = CUSTOM_REGISTRY # candidate regex for matching, in order of preference uri_regexes = [_reduced_uri_no_ns_re, _default_uri_re] # Be absolutely sure there are no comments image = image.split('#')[0] if not uri: uri = get_image_uri(image, quiet=True) # docker images require slightly different rules if uri == "docker://": uri_regexes = [_docker_uri_re] image = remove_image_uri(image, uri) for r in uri_regexes: match = r.match(image) if match: break if not match: bot.error('Could not parse image "%s"! Exiting.' % image) sys.exit(1) registry = match.group('registry') namespace = match.group('namespace') repo_name = match.group('repo') repo_tag = match.group('tag') version = match.group('version') if namespace: # strip trailing / namespace = namespace.rstrip('/') # repo_name is required and enforced by the re (in theory) # if not provided, re should not match assert (repo_name) # replace empty fields with defaults if not namespace: # for docker, if a registry was specified, don't # inject a namespace in between if registry and uri == "docker://": namespace = "" else: namespace = default_namespace if not registry: registry = default_registry if not repo_tag: repo_tag = default_tag # version is not mandatory, don't check that if not quiet: bot.verbose("Registry: %s" % registry) bot.verbose("Namespace: %s" % namespace) bot.verbose("Repo Name: %s" % repo_name) bot.verbose("Repo Tag: %s" % repo_tag) bot.verbose("Version: %s" % version) parsed = { 'registry': registry, 'namespace': namespace, 'repo_name': repo_name, 'repo_tag': repo_tag, 'version': version } return parsed
def IMPORT(image, auth=None, layerfile=None): '''IMPORT is the main script that will obtain docker layers, runscript information (either entrypoint or cmd), and environment and return a list of tarballs to extract into the image :param auth: if needed, an authentication header (default None) :param layerfile: The file to write layers to extract into ''' bot.debug("Starting Docker IMPORT, includes env, runscript, and metadata.") bot.verbose("Docker image: %s" % image) # Does the user want to override default of using ENTRYPOINT? if INCLUDE_CMD: bot.verbose2("Specified Docker CMD as %runscript.") else: bot.verbose2("Specified Docker ENTRYPOINT as %runscript.") # Input Parsing ---------------------------- # Parse image name, repo name, and namespace client = DockerApiConnection(image=image, auth=auth) docker_image_uri = "Docker image path: %s" % client.assemble_uri("/") bot.info(docker_image_uri) # IMAGE METADATA ------------------------------------------- # Use Docker Registry API (version 2.0) to get images ids, manifest images = client.get_images() # DOWNLOAD LAYERS ------------------------------------------- # Each is a .tar.gz file, obtained from registry with curl # Get the cache (or temporary one) for docker cache_base = get_cache(subfolder="docker") download_client = MultiProcess() # Generate a queue of tasks to run with MultiProcess layers = [] tasks = [] for ii in range(len(images)): image_id = images[ii] targz = "%s/%s.tar.gz" % (cache_base, image_id) if not os.path.exists(targz): tasks.append((client, image_id, cache_base)) layers.append(targz) # Does the user want to change permissions of tar? func2 = None if PLUGIN_FIXPERMS: func2 = change_permissions if len(tasks) > 0: download_layers = download_client.run(func=download_layer, func2=func2, tasks=tasks) # Get Docker runscript runscript = extract_runscript(manifest=client.manifestv1, includecmd=INCLUDE_CMD) # Add the environment export tar_file = extract_metadata_tar(client.manifestv1, client.assemble_uri(), runscript=runscript) bot.verbose2('Tar file with Docker env and labels: %s' % tar_file) # Write all layers to the layerfile if layerfile is not None: bot.verbose3("Writing Docker layers files to %s" % layerfile) write_file(layerfile, "\n".join(layers), mode="w") if tar_file is not None: write_file(layerfile, "\n%s" % tar_file, mode="a") # Return additions dictionary additions = {"layers": layers, "image": image, "manifest": client.manifest, "manifestv1": client.manifestv1, "cache_base": cache_base, "metadata": tar_file} bot.debug("*** FINISHING DOCKER IMPORT PYTHON PORTION ****\n") return additions
def IMPORT(image, auth=None, layerfile=None): '''IMPORT is the main script that will obtain docker layers, runscript information (either entrypoint or cmd), and environment and return a list of tarballs to extract into the image :param auth: if needed, an authentication header (default None) :param layerfile: The file to write layers to extract into ''' bot.debug("Starting Docker IMPORT, includes env, runscript, and metadata.") bot.verbose("Docker image: %s" % image) # Does the user want to override default of using ENTRYPOINT? if INCLUDE_CMD: bot.verbose2("Specified Docker CMD as %runscript.") else: bot.verbose2("Specified Docker ENTRYPOINT as %runscript.") # Input Parsing ---------------------------- # Parse image name, repo name, and namespace client = DockerApiConnection(image=image, auth=auth) docker_image_uri = "Docker image path: %s" % client.assemble_uri("/") bot.info(docker_image_uri) # IMAGE METADATA ------------------------------------------- # Use Docker Registry API (version 2.0) to get images ids, manifest images = client.get_images() # DOWNLOAD LAYERS ------------------------------------------- # Each is a .tar.gz file, obtained from registry with curl # Get the cache (or temporary one) for docker cache_base = get_cache(subfolder="docker") download_client = MultiProcess() # Generate a queue of tasks to run with MultiProcess layers = [] tasks = [] for ii in range(len(images)): image_id = images[ii] targz = "%s/%s.tar.gz" % (cache_base, image_id) if not os.path.exists(targz): tasks.append((client, image_id, cache_base)) layers.append(targz) # Does the user want to change permissions of tar? func2 = None if PLUGIN_FIXPERMS: func2 = change_permissions if len(tasks) > 0: download_layers = download_client.run(func=download_layer, func2=func2, tasks=tasks) # Get Docker runscript runscript = extract_runscript(manifest=client.manifestv1, includecmd=INCLUDE_CMD) # Add the environment export tar_file = extract_metadata_tar(client.manifestv1, client.assemble_uri(), runscript=runscript) bot.verbose2('Tar file with Docker env and labels: %s' % tar_file) # Write all layers to the layerfile if layerfile is not None: bot.verbose3("Writing Docker layers files to %s" % layerfile) write_file(layerfile, "\n".join(layers), mode="w") if tar_file is not None: write_file(layerfile, "\n%s" % tar_file, mode="a") # Return additions dictionary additions = { "layers": layers, "image": image, "manifest": client.manifest, "manifestv1": client.manifestv1, "cache_base": cache_base, "metadata": tar_file } bot.debug("*** FINISHING DOCKER IMPORT PYTHON PORTION ****\n") return additions
def parse_image_uri(image, uri=None, quiet=False): '''parse_image_uri will return a json structure with a registry, repo name, tag, and namespace, intended for Docker. :param image: the string provided on command line for the image name, eg: ubuntu:latest :param uri: the uri (eg, docker:// to remove), default uses "" ::note uri is maintained as a variable so we have some control over allowed :returns parsed: a json structure with repo_name, repo_tag, and namespace ''' if uri == None: uri = "" # Be absolutely sure there are not comments image = image.split('#')[0] # Get rid of any uri, and split the tag image = image.replace(uri, '') # Does the uri have a digest or Github tag (version)? image = image.split('@') version = None if len(image) == 2: version = image[1] image = image[0] image = image.split(':') # If there are three parts, we have port and tag if len(image) == 3: repo_tag = image[2] image = "%s:%s" % (image[0], image[1]) # If there are two parts, we have port or tag elif len(image) == 2: # If there isn't a slash in second part, we have a tag if image[1].find("/") == -1: repo_tag = image[1] image = image[0] # Otherwise we have a port and we merge the path else: image = "%s:%s" % (image[0], image[1]) repo_tag = default_tag else: image = image[0] repo_tag = default_tag # Now look for registry, namespace, repo image = image.split('/') if len(image) == 3: registry = image[0] namespace = image[1] repo_name = image[2] elif len(image) == 2: registry = default_registry namespace = image[0] repo_name = image[1] else: registry = default_registry namespace = default_namespace repo_name = image[0] if not quiet: bot.verbose("Registry: %s" % registry) bot.verbose("Namespace: %s" % namespace) bot.verbose("Repo Name: %s" % repo_name) bot.verbose("Repo Tag: %s" % repo_tag) bot.verbose("Version: %s" % version) parsed = { 'registry': registry, 'namespace': namespace, 'repo_name': repo_name, 'repo_tag': repo_tag } # No field should be empty for fieldname, value in parsed.items(): if len(value) == 0: bot.error("%s found empty, check uri! Exiting." % value) sys.exit(1) # Version is not required parsed['version'] = version return parsed
def parse_image_uri(image,uri=None,quiet=False): '''parse_image_uri will return a json structure with a registry, repo name, tag, and namespace, intended for Docker. :param image: the string provided on command line for the image name, eg: ubuntu:latest :param uri: the uri (eg, docker:// to remove), default uses "" ::note uri is maintained as a variable so we have some control over allowed :returns parsed: a json structure with repo_name, repo_tag, and namespace ''' if uri == None: uri = "" # Be absolutely sure there are not comments image = image.split('#')[0] # Get rid of any uri, and split the tag image = image.replace(uri,'') # Does the uri have a digest or Github tag (version)? image = image.split('@') version = None if len(image) == 2: version = image[1] image = image[0] image = image.split(':') # If there are three parts, we have port and tag if len(image) == 3: repo_tag = image[2] image = "%s:%s" %(image[0],image[1]) # If there are two parts, we have port or tag elif len(image) == 2: # If there isn't a slash in second part, we have a tag if image[1].find("/") == -1: repo_tag = image[1] image = image[0] # Otherwise we have a port and we merge the path else: image = "%s:%s" %(image[0],image[1]) repo_tag = default_tag else: image = image[0] repo_tag = default_tag # Now look for registry, namespace, repo image = image.split('/') if len(image) == 3: registry = image[0] namespace = image[1] repo_name = image[2] elif len(image) == 2: registry = default_registry namespace = image[0] repo_name = image[1] else: registry = default_registry namespace = default_namespace repo_name = image[0] if not quiet: bot.verbose("Registry: %s" %registry) bot.verbose("Namespace: %s" %namespace) bot.verbose("Repo Name: %s" %repo_name) bot.verbose("Repo Tag: %s" %repo_tag) bot.verbose("Version: %s" %version) parsed = {'registry':registry, 'namespace':namespace, 'repo_name':repo_name, 'repo_tag':repo_tag } # No field should be empty for fieldname,value in parsed.items(): if len(value) == 0: bot.error("%s found empty, check uri! Exiting." %value) sys.exit(1) # Version is not required parsed['version'] = version return parsed