mirror of
https://github.com/containers/podman.git
synced 2025-05-17 23:26:08 +08:00

This requires updating all import paths throughout, and a matching buildah update to interoperate. I can't figure out the reason for go.mod tracking github.com/containers/image v3.0.2+incompatible // indirect ((go mod graph) lists it as a direct dependency of libpod, but (go list -json -m all) lists it as an indirect dependency), but at least looking at the vendor subdirectory, it doesn't seem to be actually used in the built binaries. Signed-off-by: Miloslav Trmač <mitr@redhat.com>
177 lines
6.0 KiB
Go
177 lines
6.0 KiB
Go
package image
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
|
|
cp "github.com/containers/image/v4/copy"
|
|
"github.com/containers/image/v4/docker/reference"
|
|
"github.com/containers/image/v4/signature"
|
|
"github.com/containers/image/v4/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, "://")
|
|
}
|
|
|
|
// ReposToMap parses the specified repotags and returns a map with repositories
|
|
// as keys and the corresponding arrays of tags as values.
|
|
func ReposToMap(repotags []string) (map[string][]string, error) {
|
|
// map format is repo -> tag
|
|
repos := make(map[string][]string)
|
|
for _, repo := range repotags {
|
|
var repository, tag string
|
|
if len(repo) > 0 {
|
|
named, err := reference.ParseNormalizedNamed(repo)
|
|
repository = named.Name()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ref, ok := named.(reference.NamedTagged); ok {
|
|
tag = ref.Tag()
|
|
} else if ref, ok := named.(reference.Canonical); ok {
|
|
tag = ref.Digest().String()
|
|
}
|
|
}
|
|
repos[repository] = append(repos[repository], tag)
|
|
}
|
|
if len(repos) == 0 {
|
|
repos["<none>"] = []string{"<none>"}
|
|
}
|
|
return repos, nil
|
|
}
|
|
|
|
// 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)
|
|
}
|