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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
// 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 client
import (
"crypto/md5"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"os"
"regexp"
"strings"
)
// IsLibraryPullRef returns true if the provided string is a valid library
// reference for a pull operation.
func IsLibraryPullRef(libraryRef string) bool {
match, _ := regexp.MatchString("^(library://)?([a-z0-9]+(?:[._-][a-z0-9]+)*/){0,2}([a-z0-9]+(?:[._-][a-z0-9]+)*)(:[a-z0-9]+(?:[._-][a-z0-9]+)*)?$", libraryRef)
return match
}
// IsLibraryPushRef returns true if the provided string is a valid library
// reference for a push operation.
func IsLibraryPushRef(libraryRef string) bool {
// For push we allow specifying multiple tags, delimited with ,
match, _ := regexp.MatchString("^(library://)?([a-z0-9]+(?:[._-][a-z0-9]+)*/){2}([a-z0-9]+(?:[._-][a-z0-9]+)*)(:[a-z0-9]+(?:[,._-][a-z0-9]+)*)?$", libraryRef)
return match
}
// IsRefPart returns true if the provided string is valid as a component of a
// library URI (i.e. a valid entity, collection etc. name)
func IsRefPart(refPart string) bool {
match, err := regexp.MatchString("^[a-z0-9]+(?:[._-][a-z0-9]+)*$", refPart)
if err != nil {
return false
}
return match
}
// IsImageHash returns true if the provided string is valid as a unique hash
// for an image
func IsImageHash(refPart string) bool {
// Legacy images will be sent with hash sha256.[a-f0-9]{64}
// SIF images will be sent with hash sif.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}
// which is the unique SIF UUID
match, err := regexp.MatchString("^((sha256\\.[a-f0-9]{64})|(sif\\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}))$", refPart)
if err != nil {
return false
}
return match
}
func ParseLibraryPath(libraryRef string) (entity string, collection string, container string, tags []string) {
libraryRef = strings.TrimPrefix(libraryRef, "library://")
refParts := strings.Split(libraryRef, "/")
switch len(refParts) {
case 3:
entity = refParts[0]
collection = refParts[1]
container = refParts[2]
case 2:
entity = ""
collection = refParts[0]
container = refParts[1]
case 1:
entity = ""
collection = ""
container = refParts[0]
default:
// malformed libraryRef; must conform to "library://entity/collection/container[:tag[,tag]...]"
entity = ""
collection = ""
container = ""
tags = []string{}
return
}
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 entity, collection, container, tags
}
// IDInSlice returns true if ID is present in the slice
func IDInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
// SliceWithoutID returns slice with specified ID removed
func SliceWithoutID(list []string, a string) []string {
var newList []string
for _, b := range list {
if b != a {
newList = append(newList, b)
}
}
return newList
}
// StringInSlice returns true if string is present in the slice
func StringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
// PrettyPrint - Debug helper, print nice json for any interface
func PrettyPrint(v interface{}) {
if b, err := json.MarshalIndent(v, "", " "); err == nil {
println(string(b))
}
}
// ImageHash returns the appropriate hash for a provided image file
//
// e.g. sif.<uuid> or sha256.<sha256>
func ImageHash(filePath string) (result string, err error) {
// Currently using sha256 always
// TODO - use sif uuid for sif files!
file, err := os.Open(filePath)
if err != nil {
return "", err
}
defer file.Close()
result, _, err = sha256sum(file)
return "sha256." + result, err
}
// sha256sum computes the sha256sum of the specified reader; caller is
// responsible for resetting file pointer. 'nBytes' indicates number of
// bytes read from reader
func sha256sum(r io.Reader) (result string, nBytes int64, err error) {
hash := sha256.New()
nBytes, err = io.Copy(hash, r)
if err != nil {
return "", 0, err
}
return hex.EncodeToString(hash.Sum(nil)), nBytes, nil
}
// md5sum computes the MD5 checksum of the specified reader; caller is
// responsible for resetting file pointer. nBytes' indicates number of
// bytes read from reader
func md5sum(r io.Reader) (result string, nBytes int64, err error) {
hash := md5.New()
nBytes, err = io.Copy(hash, r)
if err != nil {
return "", 0, err
}
return hex.EncodeToString(hash.Sum(nil)), nBytes, nil
}
|