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