github.com/apptainer/singularity@v3.1.1+incompatible/internal/pkg/util/uri/uri.go (about)

     1  // Copyright (c) 2018, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  package uri
     7  
     8  import (
     9  	"fmt"
    10  	"strings"
    11  )
    12  
    13  const (
    14  	// Library is the keyword for a library ref
    15  	Library = "library"
    16  	// Shub is the keyword for a shub ref
    17  	Shub = "shub"
    18  	// HTTP is the keyword for http ref
    19  	HTTP = "http"
    20  	// HTTPS is the keyword for https ref
    21  	HTTPS = "https"
    22  )
    23  
    24  // validURIs contains a list of known uris
    25  var validURIs = map[string]bool{
    26  	"library":        true,
    27  	"shub":           true,
    28  	"docker":         true,
    29  	"docker-archive": true,
    30  	"docker-daemon":  true,
    31  	"oci":            true,
    32  	"oci-archive":    true,
    33  	"http":           true,
    34  	"https":          true,
    35  }
    36  
    37  // IsValid returns whether or not the given source is valid
    38  func IsValid(source string) (valid bool, err error) {
    39  
    40  	u := strings.SplitN(source, ":", 2)
    41  
    42  	if len(u) != 2 {
    43  		return false, fmt.Errorf("Invalid URI %s", source)
    44  	}
    45  
    46  	if _, ok := validURIs[u[0]]; ok {
    47  		return true, nil
    48  	}
    49  
    50  	return false, fmt.Errorf("Invalid URI %s", source)
    51  }
    52  
    53  // GetName turns a transport:ref URI into a name containing the top-level identifier
    54  // of the image. For example, docker://godlovedc/lolcow returns lolcow
    55  //
    56  // Returns "" when not in transport:ref format
    57  func GetName(uri string) string {
    58  	transport, ref := Split(uri)
    59  	if transport == "" {
    60  		return ""
    61  	}
    62  
    63  	ref = strings.TrimLeft(ref, "/")    // Trim leading "/" characters
    64  	refSplit := strings.Split(ref, "/") // Split ref into parts
    65  
    66  	if transport == HTTP || transport == HTTPS {
    67  		imageName := refSplit[len(refSplit)-1]
    68  		return imageName
    69  	}
    70  
    71  	// Default tag is latest
    72  	tags := []string{"latest"}
    73  	container := refSplit[len(refSplit)-1]
    74  
    75  	if strings.Contains(container, ":") {
    76  		imageParts := strings.Split(container, ":")
    77  		container = imageParts[0]
    78  		tags = []string{imageParts[1]}
    79  		if strings.Contains(tags[0], ",") {
    80  			tags = strings.Split(tags[0], ",")
    81  		}
    82  	}
    83  
    84  	return fmt.Sprintf("%s_%s.sif", container, tags[0])
    85  }
    86  
    87  // Split splits a URI into it's components which can be used directly through containers/image
    88  //
    89  // This can be tricky if there is no type but a file name contains a colon.
    90  //
    91  // Examples:
    92  //   docker://ubuntu -> docker, //ubuntu
    93  //   docker://ubuntu:18.04 -> docker, //ubuntu:18.04
    94  //   oci-archive:path/to/archive -> oci-archive, path/to/archive
    95  //   ubuntu -> "", ubuntu
    96  //   ubuntu:18.04.img -> "", ubuntu:18.04.img
    97  func Split(uri string) (transport string, ref string) {
    98  	uriSplit := strings.SplitN(uri, ":", 2)
    99  	if len(uriSplit) == 1 {
   100  		// no colon
   101  		return "", uri
   102  	}
   103  
   104  	if uriSplit[1][0:1] == "//" {
   105  		// the format was ://, so try it whether or not valid URI
   106  		return uriSplit[0], uriSplit[1]
   107  	}
   108  
   109  	if ok, err := IsValid(uri); ok && err == nil {
   110  		// also accept recognized URIs
   111  		return uriSplit[0], uriSplit[1]
   112  	}
   113  
   114  	return "", uri
   115  }