github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/libpod/image/utils.go (about) 1 package image 2 3 import ( 4 "fmt" 5 "io" 6 "net/url" 7 "regexp" 8 "strings" 9 10 cp "github.com/containers/image/v5/copy" 11 "github.com/containers/image/v5/docker/reference" 12 "github.com/containers/image/v5/signature" 13 "github.com/containers/image/v5/types" 14 "github.com/containers/storage" 15 "github.com/pkg/errors" 16 ) 17 18 // findImageInRepotags takes an imageParts struct and searches images' repotags for 19 // a match on name:tag 20 func findImageInRepotags(search imageParts, images []*Image) (*storage.Image, error) { 21 _, searchName, searchSuspiciousTagValueForSearch := search.suspiciousRefNameTagValuesForSearch() 22 var results []*storage.Image 23 for _, image := range images { 24 for _, name := range image.Names() { 25 d, err := decompose(name) 26 // if we get an error, ignore and keep going 27 if err != nil { 28 continue 29 } 30 _, dName, dSuspiciousTagValueForSearch := d.suspiciousRefNameTagValuesForSearch() 31 if dName == searchName && dSuspiciousTagValueForSearch == searchSuspiciousTagValueForSearch { 32 results = append(results, image.image) 33 continue 34 } 35 // account for registry:/somedir/image 36 if strings.HasSuffix(dName, searchName) && dSuspiciousTagValueForSearch == searchSuspiciousTagValueForSearch { 37 results = append(results, image.image) 38 continue 39 } 40 } 41 } 42 if len(results) == 0 { 43 return &storage.Image{}, errors.Errorf("unable to find a name and tag match for %s in repotags", searchName) 44 } else if len(results) > 1 { 45 return &storage.Image{}, errors.Errorf("found multiple name and tag matches for %s in repotags", searchName) 46 } 47 return results[0], nil 48 } 49 50 // getCopyOptions constructs a new containers/image/copy.Options{} struct from the given parameters, inheriting some from sc. 51 func getCopyOptions(sc *types.SystemContext, reportWriter io.Writer, srcDockerRegistry, destDockerRegistry *DockerRegistryOptions, signing SigningOptions, manifestType string, additionalDockerArchiveTags []reference.NamedTagged) *cp.Options { 52 if srcDockerRegistry == nil { 53 srcDockerRegistry = &DockerRegistryOptions{} 54 } 55 if destDockerRegistry == nil { 56 destDockerRegistry = &DockerRegistryOptions{} 57 } 58 srcContext := srcDockerRegistry.GetSystemContext(sc, additionalDockerArchiveTags) 59 destContext := destDockerRegistry.GetSystemContext(sc, additionalDockerArchiveTags) 60 return &cp.Options{ 61 RemoveSignatures: signing.RemoveSignatures, 62 SignBy: signing.SignBy, 63 ReportWriter: reportWriter, 64 SourceCtx: srcContext, 65 DestinationCtx: destContext, 66 ForceManifestMIMEType: manifestType, 67 } 68 } 69 70 // getPolicyContext sets up, initializes and returns a new context for the specified policy 71 func getPolicyContext(ctx *types.SystemContext) (*signature.PolicyContext, error) { 72 policy, err := signature.DefaultPolicy(ctx) 73 if err != nil { 74 return nil, err 75 } 76 77 policyContext, err := signature.NewPolicyContext(policy) 78 if err != nil { 79 return nil, err 80 } 81 return policyContext, nil 82 } 83 84 // hasTransport determines if the image string contains '://', returns bool 85 func hasTransport(image string) bool { 86 return strings.Contains(image, "://") 87 } 88 89 // ReposToMap parses the specified repotags and returns a map with repositories 90 // as keys and the corresponding arrays of tags or digests-as-strings as values. 91 func ReposToMap(names []string) (map[string][]string, error) { 92 // map format is repo -> []tag-or-digest 93 repos := make(map[string][]string) 94 for _, name := range names { 95 var repository, tag string 96 if len(name) > 0 { 97 named, err := reference.ParseNormalizedNamed(name) 98 if err != nil { 99 return nil, err 100 } 101 repository = named.Name() 102 if ref, ok := named.(reference.NamedTagged); ok { 103 tag = ref.Tag() 104 } else if ref, ok := named.(reference.Canonical); ok { 105 tag = ref.Digest().String() 106 } 107 } 108 repos[repository] = append(repos[repository], tag) 109 } 110 if len(repos) == 0 { 111 repos["<none>"] = []string{"<none>"} 112 } 113 return repos, nil 114 } 115 116 // GetAdditionalTags returns a list of reference.NamedTagged for the 117 // additional tags given in images 118 func GetAdditionalTags(images []string) ([]reference.NamedTagged, error) { 119 var allTags []reference.NamedTagged 120 for _, img := range images { 121 ref, err := reference.ParseNormalizedNamed(img) 122 if err != nil { 123 return nil, errors.Wrapf(err, "error parsing additional tags") 124 } 125 refTagged, isTagged := ref.(reference.NamedTagged) 126 if isTagged { 127 allTags = append(allTags, refTagged) 128 } 129 } 130 return allTags, nil 131 } 132 133 // IsValidImageURI checks if image name has valid format 134 func IsValidImageURI(imguri string) (bool, error) { 135 uri := "http://" + imguri 136 u, err := url.Parse(uri) 137 if err != nil { 138 return false, errors.Wrapf(err, "invalid image uri: %s", imguri) 139 } 140 reg := regexp.MustCompile(`^[a-zA-Z0-9-_\.]+\/?:?[0-9]*[a-z0-9-\/:]*$`) 141 ret := reg.FindAllString(u.Host, -1) 142 if len(ret) == 0 { 143 return false, errors.Wrapf(err, "invalid image uri: %s", imguri) 144 } 145 reg = regexp.MustCompile(`^[a-z0-9-:\./]*$`) 146 ret = reg.FindAllString(u.Fragment, -1) 147 if len(ret) == 0 { 148 return false, errors.Wrapf(err, "invalid image uri: %s", imguri) 149 } 150 return true, nil 151 } 152 153 // imageNameForSaveDestination returns a Docker-like reference appropriate for saving img, 154 // which the user referred to as imgUserInput; or an empty string, if there is no appropriate 155 // reference. 156 func imageNameForSaveDestination(img *Image, imgUserInput string) string { 157 if strings.Contains(img.ID(), imgUserInput) { 158 return "" 159 } 160 161 prepend := "" 162 localRegistryPrefix := fmt.Sprintf("%s/", DefaultLocalRegistry) 163 if !strings.HasPrefix(imgUserInput, localRegistryPrefix) { 164 // we need to check if localhost was added to the image name in NewFromLocal 165 for _, name := range img.Names() { 166 // If the user is saving an image in the localhost registry, getLocalImage need 167 // a name that matches the format localhost/<tag1>:<tag2> or localhost/<tag>:latest to correctly 168 // set up the manifest and save. 169 if strings.HasPrefix(name, localRegistryPrefix) && (strings.HasSuffix(name, imgUserInput) || strings.HasSuffix(name, fmt.Sprintf("%s:latest", imgUserInput))) { 170 prepend = localRegistryPrefix 171 break 172 } 173 } 174 } 175 return fmt.Sprintf("%s%s", prepend, imgUserInput) 176 }