File: uri.go

package info (click to toggle)
singularity-container 4.0.3%2Bds1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 21,672 kB
  • sloc: asm: 3,857; sh: 2,125; ansic: 1,677; awk: 414; makefile: 110; python: 99
file content (119 lines) | stat: -rw-r--r-- 3,102 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright (c) 2018-2022, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package uri

import (
	"fmt"
	"strings"
)

const (
	// Library is the keyword for a library ref
	Library = "library"
	// Shub is the keyword for a shub ref
	Shub = "shub"
	// HTTP is the keyword for http ref
	HTTP = "http"
	// HTTPS is the keyword for https ref
	HTTPS = "https"
	// Oras is the keyword for an oras ref
	Oras = "oras"
)

// validURIs contains a list of known uris
var validURIs = map[string]bool{
	"library":        true,
	"shub":           true,
	"docker":         true,
	"docker-archive": true,
	"docker-daemon":  true,
	"oci":            true,
	"oci-archive":    true,
	"http":           true,
	"https":          true,
	"oras":           true,
}

// IsValid returns whether or not the given source is valid
func IsValid(source string) (valid bool, err error) {
	u := strings.SplitN(source, ":", 2)

	if len(u) != 2 {
		return false, fmt.Errorf("invalid uri %s", source)
	}

	if _, ok := validURIs[u[0]]; ok {
		return true, nil
	}

	return false, fmt.Errorf("invalid uri %s", source)
}

// Filename turns a transport:ref URI into a filename containing the top-level
// identifier of the image. For example, docker://sylabsio/lolcow:latest returns
// lolcow_latest.<suffix>
//
// Returns "" when not in transport:ref format
func Filename(uri string, suffix string) string {
	transport, ref := Split(uri)
	if transport == "" {
		return ""
	}

	ref = strings.TrimLeft(ref, "/")    // Trim leading "/" characters
	refSplit := strings.Split(ref, "/") // Split ref into parts

	if transport == HTTP || transport == HTTPS {
		imageName := refSplit[len(refSplit)-1]
		return imageName
	}

	// Default tag is latest
	tags := []string{"latest"}
	container := refSplit[len(refSplit)-1]

	if strings.Contains(container, ":") {
		imageParts := strings.Split(container, ":")
		container = imageParts[0]
		tags = []string{imageParts[1]}
		if strings.Contains(tags[0], ",") {
			tags = strings.Split(tags[0], ",")
		}
	}

	return fmt.Sprintf("%s_%s.%s", container, tags[0], suffix)
}

// Split splits a URI into it's components which can be used directly through containers/image
//
// This can be tricky if there is no type but a file name contains a colon.
//
// Examples:
//
//	docker://ubuntu -> docker, //ubuntu
//	docker://ubuntu:18.04 -> docker, //ubuntu:18.04
//	oci-archive:path/to/archive -> oci-archive, path/to/archive
//	ubuntu -> "", ubuntu
//	ubuntu:18.04.img -> "", ubuntu:18.04.img
func Split(uri string) (transport string, ref string) {
	uriSplit := strings.SplitN(uri, ":", 2)
	if len(uriSplit) == 1 {
		// no colon
		return "", uri
	}

	if strings.HasPrefix(uriSplit[1], "//") {
		// the format was ://, so try it whether or not valid URI
		return uriSplit[0], uriSplit[1]
	}

	if ok, err := IsValid(uri); ok && err == nil {
		// also accept recognized URIs
		return uriSplit[0], uriSplit[1]
	}

	return "", uri
}