github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/libpod/image/parts.go (about)

     1  package image
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/containers/image/v5/docker/reference"
     7  	"github.com/pkg/errors"
     8  )
     9  
    10  // imageParts describes the parts of an image's name
    11  type imageParts struct {
    12  	unnormalizedRef reference.Named // WARNING: Did not go through docker.io[/library] normalization
    13  	hasRegistry     bool
    14  }
    15  
    16  // Registries must contain a ":" or a "." or be localhost; this helper exists for users of reference.Parse.
    17  // For inputs that should use the docker.io[/library] normalization, use reference.ParseNormalizedNamed instead.
    18  func isRegistry(name string) bool {
    19  	return strings.ContainsAny(name, ".:") || name == "localhost"
    20  }
    21  
    22  // GetImageBaseName uses decompose and string splits to obtain the base
    23  // name of an image.  Doing this here because it beats changing the
    24  // imageParts struct names to be exported as well.
    25  func GetImageBaseName(input string) (string, error) {
    26  	decomposedImage, err := decompose(input)
    27  	if err != nil {
    28  		return "", err
    29  	}
    30  	splitImageName := strings.Split(decomposedImage.unnormalizedRef.Name(), "/")
    31  	return splitImageName[len(splitImageName)-1], nil
    32  }
    33  
    34  // decompose breaks an input name into an imageParts description
    35  func decompose(input string) (imageParts, error) {
    36  	imgRef, err := reference.Parse(input)
    37  	if err != nil {
    38  		return imageParts{}, err
    39  	}
    40  	unnormalizedNamed := imgRef.(reference.Named)
    41  	// ip.unnormalizedRef, because it uses reference.Parse and not reference.ParseNormalizedNamed,
    42  	// does not use the standard heuristics for domains vs. namespaces/repos, so we need to check
    43  	// explicitly.
    44  	hasRegistry := isRegistry(reference.Domain(unnormalizedNamed))
    45  	return imageParts{
    46  		unnormalizedRef: unnormalizedNamed,
    47  		hasRegistry:     hasRegistry,
    48  	}, nil
    49  }
    50  
    51  // suspiciousRefNameTagValuesForSearch returns a "tag" value used in a previous implementation.
    52  // This exists only to preserve existing behavior in heuristic code; it’s dubious that that behavior is correct,
    53  // gespecially for the tag value.
    54  func (ip *imageParts) suspiciousRefNameTagValuesForSearch() (string, string, string) {
    55  	registry := reference.Domain(ip.unnormalizedRef)
    56  	imageName := reference.Path(ip.unnormalizedRef)
    57  	// ip.unnormalizedRef, because it uses reference.Parse and not reference.ParseNormalizedNamed,
    58  	// does not use the standard heuristics for domains vs. namespaces/repos.
    59  	if registry != "" && !isRegistry(registry) {
    60  		imageName = registry + "/" + imageName
    61  		registry = ""
    62  	}
    63  
    64  	var tag string
    65  	if tagged, isTagged := ip.unnormalizedRef.(reference.NamedTagged); isTagged {
    66  		tag = tagged.Tag()
    67  	} else if _, hasDigest := ip.unnormalizedRef.(reference.Digested); hasDigest {
    68  		tag = "none"
    69  	} else {
    70  		tag = LatestTag
    71  	}
    72  	return registry, imageName, tag
    73  }
    74  
    75  // referenceWithRegistry returns a (normalized) reference.Named composed of ip (with !ip.hasRegistry)
    76  // qualified with registry.
    77  func (ip *imageParts) referenceWithRegistry(registry string) (reference.Named, error) {
    78  	if ip.hasRegistry {
    79  		return nil, errors.Errorf("internal error: referenceWithRegistry called on imageParts with a registry (%#v)", *ip)
    80  	}
    81  	// We could build a reference.WithName+WithTag/WithDigest here, but we need to round-trip via a string
    82  	// and a ParseNormalizedNamed anyway to get the right normalization of docker.io/library, so
    83  	// just use a string directly.
    84  	qualified := registry + "/" + ip.unnormalizedRef.String()
    85  	ref, err := reference.ParseNormalizedNamed(qualified)
    86  	if err != nil {
    87  		return nil, errors.Wrapf(err, "error normalizing registry+unqualified reference %#v", qualified)
    88  	}
    89  	return ref, nil
    90  }
    91  
    92  // normalizedReference returns a (normalized) reference for ip (with ip.hasRegistry)
    93  func (ip *imageParts) normalizedReference() (reference.Named, error) {
    94  	if !ip.hasRegistry {
    95  		return nil, errors.Errorf("internal error: normalizedReference called on imageParts without a registry (%#v)", *ip)
    96  	}
    97  	// We need to round-trip via a string to get the right normalization of docker.io/library
    98  	s := ip.unnormalizedRef.String()
    99  	ref, err := reference.ParseNormalizedNamed(s)
   100  	if err != nil { // Should never happen
   101  		return nil, errors.Wrapf(err, "error normalizing qualified reference %#v", s)
   102  	}
   103  	return ref, nil
   104  }