Files
podman/libpod/image/utils.go
Valentin Rothberg ecae5f7ade image list: return all associated names
Always return all associated names / repo tags of an image and fix a bug
with malformed repo tags.

Previously, Podman returned all names only with `--all` but this flag
only instructs to list intermediate images and should not alter
associated names.  With `--all` Podman queried the repo tags of an image
which splits all *tagged* names into repository and tag which is then
reassembled to eventually be parsed again in the frontend.  Lot's of
redundant CPU heat and buggy as the reassembly didn't consider digests
which ultimately broke parsing in the frontend.

Fixes: #7651
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2020-09-17 12:23:58 +02:00

150 lines
5.2 KiB
Go

package image
import (
"fmt"
"io"
"net/url"
"regexp"
"strings"
cp "github.com/containers/image/v5/copy"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/signature"
"github.com/containers/image/v5/types"
"github.com/containers/storage"
"github.com/pkg/errors"
)
// findImageInRepotags takes an imageParts struct and searches images' repotags for
// a match on name:tag
func findImageInRepotags(search imageParts, images []*Image) (*storage.Image, error) {
_, searchName, searchSuspiciousTagValueForSearch := search.suspiciousRefNameTagValuesForSearch()
var results []*storage.Image
for _, image := range images {
for _, name := range image.Names() {
d, err := decompose(name)
// if we get an error, ignore and keep going
if err != nil {
continue
}
_, dName, dSuspiciousTagValueForSearch := d.suspiciousRefNameTagValuesForSearch()
if dName == searchName && dSuspiciousTagValueForSearch == searchSuspiciousTagValueForSearch {
results = append(results, image.image)
continue
}
// account for registry:/somedir/image
if strings.HasSuffix(dName, searchName) && dSuspiciousTagValueForSearch == searchSuspiciousTagValueForSearch {
results = append(results, image.image)
continue
}
}
}
if len(results) == 0 {
return &storage.Image{}, errors.Errorf("unable to find a name and tag match for %s in repotags", searchName)
} else if len(results) > 1 {
return &storage.Image{}, errors.Errorf("found multiple name and tag matches for %s in repotags", searchName)
}
return results[0], nil
}
// getCopyOptions constructs a new containers/image/copy.Options{} struct from the given parameters, inheriting some from sc.
func getCopyOptions(sc *types.SystemContext, reportWriter io.Writer, srcDockerRegistry, destDockerRegistry *DockerRegistryOptions, signing SigningOptions, manifestType string, additionalDockerArchiveTags []reference.NamedTagged) *cp.Options {
if srcDockerRegistry == nil {
srcDockerRegistry = &DockerRegistryOptions{}
}
if destDockerRegistry == nil {
destDockerRegistry = &DockerRegistryOptions{}
}
srcContext := srcDockerRegistry.GetSystemContext(sc, additionalDockerArchiveTags)
destContext := destDockerRegistry.GetSystemContext(sc, additionalDockerArchiveTags)
return &cp.Options{
RemoveSignatures: signing.RemoveSignatures,
SignBy: signing.SignBy,
ReportWriter: reportWriter,
SourceCtx: srcContext,
DestinationCtx: destContext,
ForceManifestMIMEType: manifestType,
}
}
// getPolicyContext sets up, initializes and returns a new context for the specified policy
func getPolicyContext(ctx *types.SystemContext) (*signature.PolicyContext, error) {
policy, err := signature.DefaultPolicy(ctx)
if err != nil {
return nil, err
}
policyContext, err := signature.NewPolicyContext(policy)
if err != nil {
return nil, err
}
return policyContext, nil
}
// hasTransport determines if the image string contains '://', returns bool
func hasTransport(image string) bool {
return strings.Contains(image, "://")
}
// GetAdditionalTags returns a list of reference.NamedTagged for the
// additional tags given in images
func GetAdditionalTags(images []string) ([]reference.NamedTagged, error) {
var allTags []reference.NamedTagged
for _, img := range images {
ref, err := reference.ParseNormalizedNamed(img)
if err != nil {
return nil, errors.Wrapf(err, "error parsing additional tags")
}
refTagged, isTagged := ref.(reference.NamedTagged)
if isTagged {
allTags = append(allTags, refTagged)
}
}
return allTags, nil
}
// IsValidImageURI checks if image name has valid format
func IsValidImageURI(imguri string) (bool, error) {
uri := "http://" + imguri
u, err := url.Parse(uri)
if err != nil {
return false, errors.Wrapf(err, "invalid image uri: %s", imguri)
}
reg := regexp.MustCompile(`^[a-zA-Z0-9-_\.]+\/?:?[0-9]*[a-z0-9-\/:]*$`)
ret := reg.FindAllString(u.Host, -1)
if len(ret) == 0 {
return false, errors.Wrapf(err, "invalid image uri: %s", imguri)
}
reg = regexp.MustCompile(`^[a-z0-9-:\./]*$`)
ret = reg.FindAllString(u.Fragment, -1)
if len(ret) == 0 {
return false, errors.Wrapf(err, "invalid image uri: %s", imguri)
}
return true, nil
}
// imageNameForSaveDestination returns a Docker-like reference appropriate for saving img,
// which the user referred to as imgUserInput; or an empty string, if there is no appropriate
// reference.
func imageNameForSaveDestination(img *Image, imgUserInput string) string {
if strings.Contains(img.ID(), imgUserInput) {
return ""
}
prepend := ""
localRegistryPrefix := fmt.Sprintf("%s/", DefaultLocalRegistry)
if !strings.HasPrefix(imgUserInput, localRegistryPrefix) {
// we need to check if localhost was added to the image name in NewFromLocal
for _, name := range img.Names() {
// If the user is saving an image in the localhost registry, getLocalImage need
// a name that matches the format localhost/<tag1>:<tag2> or localhost/<tag>:latest to correctly
// set up the manifest and save.
if strings.HasPrefix(name, localRegistryPrefix) && (strings.HasSuffix(name, imgUserInput) || strings.HasSuffix(name, fmt.Sprintf("%s:latest", imgUserInput))) {
prepend = localRegistryPrefix
break
}
}
}
return fmt.Sprintf("%s%s", prepend, imgUserInput)
}