summaryrefslogtreecommitdiff
path: root/libpod/image
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/image')
-rw-r--r--libpod/image/docker_registry_options.go13
-rw-r--r--libpod/image/image.go42
-rw-r--r--libpod/image/pull.go294
-rw-r--r--libpod/image/pull_test.go320
-rw-r--r--libpod/image/testdata/docker-name-only.tar.xzbin0 -> 1024 bytes
-rw-r--r--libpod/image/testdata/docker-registry-name.tar.xzbin0 -> 1028 bytes
-rw-r--r--libpod/image/testdata/docker-two-images.tar.xzbin0 -> 1416 bytes
-rw-r--r--libpod/image/testdata/docker-two-names.tar.xzbin0 -> 1040 bytes
-rw-r--r--libpod/image/testdata/docker-unnamed.tar.xzbin0 -> 968 bytes
-rw-r--r--libpod/image/testdata/oci-name-only.tar.gzbin0 -> 975 bytes
-rw-r--r--libpod/image/testdata/oci-non-docker-name.tar.gzbin0 -> 991 bytes
-rw-r--r--libpod/image/testdata/oci-registry-name.tar.gzbin0 -> 979 bytes
-rw-r--r--libpod/image/testdata/oci-unnamed.tar.gzbin0 -> 928 bytes
-rw-r--r--libpod/image/utils.go8
14 files changed, 471 insertions, 206 deletions
diff --git a/libpod/image/docker_registry_options.go b/libpod/image/docker_registry_options.go
index 838edf2d0..97a151396 100644
--- a/libpod/image/docker_registry_options.go
+++ b/libpod/image/docker_registry_options.go
@@ -23,18 +23,19 @@ type DockerRegistryOptions struct {
DockerInsecureSkipTLSVerify bool
}
-// GetSystemContext constructs a new system context from the given signaturePolicy path and the
-// values in the DockerRegistryOptions
-func (o DockerRegistryOptions) GetSystemContext(signaturePolicyPath, authFile string, forceCompress bool, additionalDockerArchiveTags []reference.NamedTagged) *types.SystemContext {
+// GetSystemContext constructs a new system context from a parent context. the values in the DockerRegistryOptions, and other parameters.
+func (o DockerRegistryOptions) GetSystemContext(parent *types.SystemContext, additionalDockerArchiveTags []reference.NamedTagged) *types.SystemContext {
sc := &types.SystemContext{
- SignaturePolicyPath: signaturePolicyPath,
DockerAuthConfig: o.DockerRegistryCreds,
DockerCertPath: o.DockerCertPath,
DockerInsecureSkipTLSVerify: o.DockerInsecureSkipTLSVerify,
- AuthFilePath: authFile,
- DirForceCompress: forceCompress,
DockerArchiveAdditionalTags: additionalDockerArchiveTags,
}
+ if parent != nil {
+ sc.SignaturePolicyPath = parent.SignaturePolicyPath
+ sc.AuthFilePath = parent.AuthFilePath
+ sc.DirForceCompress = parent.DirForceCompress
+ }
return sc
}
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 112eeb015..9447ec9e1 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -15,6 +15,7 @@ import (
"github.com/containers/image/manifest"
is "github.com/containers/image/storage"
"github.com/containers/image/tarball"
+ "github.com/containers/image/transports"
"github.com/containers/image/transports/alltransports"
"github.com/containers/image/types"
"github.com/containers/storage"
@@ -144,7 +145,7 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile
if signaturePolicyPath == "" {
signaturePolicyPath = ir.SignaturePolicyPath
}
- imageName, err := newImage.pullImage(ctx, writer, authfile, signaturePolicyPath, signingoptions, dockeroptions, forceSecure)
+ imageName, err := ir.pullImageFromHeuristicSource(ctx, name, writer, authfile, signaturePolicyPath, signingoptions, dockeroptions, forceSecure)
if err != nil {
return nil, errors.Wrapf(err, "unable to pull %s", name)
}
@@ -158,22 +159,17 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile
return &newImage, nil
}
-// LoadFromArchive creates a new image object for images pulled from a tar archive (podman load)
+// LoadFromArchiveReference creates a new image object for images pulled from a tar archive and the like (podman load)
// This function is needed because it is possible for a tar archive to have multiple tags for one image
-func (ir *Runtime) LoadFromArchive(ctx context.Context, name, signaturePolicyPath string, writer io.Writer) ([]*Image, error) {
+func (ir *Runtime) LoadFromArchiveReference(ctx context.Context, srcRef types.ImageReference, signaturePolicyPath string, writer io.Writer) ([]*Image, error) {
var newImages []*Image
- newImage := Image{
- InputName: name,
- Local: false,
- imageruntime: ir,
- }
if signaturePolicyPath == "" {
signaturePolicyPath = ir.SignaturePolicyPath
}
- imageNames, err := newImage.pullImage(ctx, writer, "", signaturePolicyPath, SigningOptions{}, &DockerRegistryOptions{}, false)
+ imageNames, err := ir.pullImageFromReference(ctx, srcRef, writer, "", signaturePolicyPath, SigningOptions{}, &DockerRegistryOptions{}, false)
if err != nil {
- return nil, errors.Wrapf(err, "unable to pull %s", name)
+ return nil, errors.Wrapf(err, "unable to pull %s", transports.ImageName(srcRef))
}
for _, name := range imageNames {
@@ -513,8 +509,9 @@ func (i *Image) UntagImage(tag string) error {
return nil
}
-// PushImage pushes the given image to a location described by the given path
-func (i *Image) PushImage(ctx context.Context, destination, manifestMIMEType, authFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, forceSecure bool, additionalDockerArchiveTags []reference.NamedTagged) error {
+// PushImageToHeuristicDestination pushes the given image to "destination", which is heuristically parsed.
+// Use PushImageToReference if the destination is known precisely.
+func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination, manifestMIMEType, authFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, forceSecure bool, additionalDockerArchiveTags []reference.NamedTagged) error {
if destination == "" {
return errors.Wrapf(syscall.EINVAL, "destination image name must be specified")
}
@@ -532,7 +529,11 @@ func (i *Image) PushImage(ctx context.Context, destination, manifestMIMEType, au
return err
}
}
+ return i.PushImageToReference(ctx, dest, manifestMIMEType, authFile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, forceSecure, additionalDockerArchiveTags)
+}
+// PushImageToReference pushes the given image to a location described by the given path
+func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageReference, manifestMIMEType, authFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, forceSecure bool, additionalDockerArchiveTags []reference.NamedTagged) error {
sc := GetSystemContext(signaturePolicyPath, authFile, forceCompress)
policyContext, err := getPolicyContext(sc)
@@ -550,13 +551,13 @@ func (i *Image) PushImage(ctx context.Context, destination, manifestMIMEType, au
if err != nil {
return err
}
- copyOptions := getCopyOptions(writer, signaturePolicyPath, nil, dockerRegistryOptions, signingOptions, authFile, manifestMIMEType, forceCompress, additionalDockerArchiveTags)
- if strings.HasPrefix(DockerTransport, dest.Transport().Name()) {
- imgRef, err := reference.Parse(dest.DockerReference().String())
- if err != nil {
- return err
+ copyOptions := getCopyOptions(sc, writer, nil, dockerRegistryOptions, signingOptions, manifestMIMEType, additionalDockerArchiveTags)
+ if dest.Transport().Name() == DockerTransport {
+ imgRef := dest.DockerReference()
+ if imgRef == nil { // This should never happen; such references can’t be created.
+ return fmt.Errorf("internal error: DockerTransport reference %s does not have a DockerReference", transports.ImageName(dest))
}
- registry := reference.Domain(imgRef.(reference.Named))
+ registry := reference.Domain(imgRef)
if util.StringInSlice(registry, insecureRegistries) && !forceSecure {
copyOptions.DestinationCtx.DockerInsecureSkipTLSVerify = true
@@ -873,8 +874,7 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) {
// Import imports and image into the store and returns an image
func (ir *Runtime) Import(ctx context.Context, path, reference string, writer io.Writer, signingOptions SigningOptions, imageConfig ociv1.Image) (*Image, error) {
- file := TarballTransport + ":" + path
- src, err := alltransports.ParseImageName(file)
+ src, err := tarball.Transport.ParseReference(path)
if err != nil {
return nil, errors.Wrapf(err, "error parsing image name %q", path)
}
@@ -906,7 +906,7 @@ func (ir *Runtime) Import(ctx context.Context, path, reference string, writer io
return nil, err
}
defer policyContext.Destroy()
- copyOptions := getCopyOptions(writer, "", nil, nil, signingOptions, "", "", false, nil)
+ copyOptions := getCopyOptions(sc, writer, nil, nil, signingOptions, "", nil)
dest, err := is.Transport.ParseStoreReference(ir.store, reference)
if err != nil {
errors.Wrapf(err, "error getting image reference for %q", reference)
diff --git a/libpod/image/pull.go b/libpod/image/pull.go
index cc60c8894..ff978d563 100644
--- a/libpod/image/pull.go
+++ b/libpod/image/pull.go
@@ -15,7 +15,7 @@ import (
ociarchive "github.com/containers/image/oci/archive"
"github.com/containers/image/pkg/sysregistries"
is "github.com/containers/image/storage"
- "github.com/containers/image/tarball"
+ "github.com/containers/image/transports"
"github.com/containers/image/transports/alltransports"
"github.com/containers/image/types"
"github.com/pkg/errors"
@@ -34,37 +34,46 @@ var (
// DirTransport is the transport for pushing and pulling
// images to and from a directory
DirTransport = directory.Transport.Name()
- // TransportNames are the supported transports in string form
- TransportNames = [...]string{DefaultTransport, DockerArchive, OCIArchive, "ostree:", "dir:"}
- // TarballTransport is the transport for importing a tar archive
- // and creating a filesystem image
- TarballTransport = tarball.Transport.Name()
// DockerTransport is the transport for docker registries
- DockerTransport = docker.Transport.Name() + "://"
+ DockerTransport = docker.Transport.Name()
// AtomicTransport is the transport for atomic registries
AtomicTransport = "atomic"
// DefaultTransport is a prefix that we apply to an image name
- DefaultTransport = DockerTransport
+ // NOTE: This is a string prefix, not actually a transport name usable for transports.Get();
+ // and because syntaxes of image names are transport-dependent, the prefix is not really interchangeable;
+ // each user implicitly assumes the appended string is a Docker-like reference.
+ DefaultTransport = DockerTransport + "://"
// DefaultLocalRepo is the default local repository for local image operations
// Remote pulls will still use defined registries
DefaultLocalRepo = "localhost"
)
-// pullRefPair records a pair of prepared image references to try to pull (if not DockerArchive) or to pull all (if DockerArchive)
+// pullRefPair records a pair of prepared image references to pull.
type pullRefPair struct {
image string
srcRef types.ImageReference
dstRef types.ImageReference
}
-// pullRefName records a prepared source reference and a destination name to try to pull (if not DockerArchive) or to pull all (if DockerArchive)
-type pullRefName struct {
- image string
- srcRef types.ImageReference
- dstName string
+// pullGoal represents the prepared image references and decided behavior to be executed by imagePull
+type pullGoal struct {
+ refPairs []pullRefPair
+ pullAllPairs bool // Pull all refPairs instead of stopping on first success.
+ usedSearchRegistries bool // refPairs construction has depended on registries.GetRegistries()
+ searchedRegistries []string // The list of search registries used; set only if usedSearchRegistries
}
-func (ir *Runtime) getPullRefPair(srcRef types.ImageReference, destName string) (*pullRefPair, error) {
+// singlePullRefPairGoal returns a no-frills pull goal for the specified reference pair.
+func singlePullRefPairGoal(rp pullRefPair) *pullGoal {
+ return &pullGoal{
+ refPairs: []pullRefPair{rp},
+ pullAllPairs: false, // Does not really make a difference.
+ usedSearchRegistries: false,
+ searchedRegistries: nil,
+ }
+}
+
+func (ir *Runtime) getPullRefPair(srcRef types.ImageReference, destName string) (pullRefPair, error) {
imgPart, err := decompose(destName)
if err == nil && !imgPart.hasRegistry {
// If the image doesn't have a registry, set it as the default repo
@@ -79,24 +88,31 @@ func (ir *Runtime) getPullRefPair(srcRef types.ImageReference, destName string)
}
destRef, err := is.Transport.ParseStoreReference(ir.store, reference)
if err != nil {
- return nil, errors.Wrapf(err, "error parsing dest reference name")
+ return pullRefPair{}, errors.Wrapf(err, "error parsing dest reference name %#v", destName)
}
- return &pullRefPair{
+ return pullRefPair{
image: destName,
srcRef: srcRef,
dstRef: destRef,
}, nil
}
-// returns a list of pullRefPair with the srcRef and DstRef based on the transport being used
-func (ir *Runtime) getPullListFromRef(ctx context.Context, srcRef types.ImageReference, imgName string, sc *types.SystemContext) ([]*pullRefPair, error) {
- var pullRefPairs []*pullRefPair
- splitArr := strings.Split(imgName, ":")
- archFile := splitArr[len(splitArr)-1]
+// getSinglePullRefPairGoal calls getPullRefPair with the specified parameters, and returns a single-pair goal for the return value.
+func (ir *Runtime) getSinglePullRefPairGoal(srcRef types.ImageReference, destName string) (*pullGoal, error) {
+ rp, err := ir.getPullRefPair(srcRef, destName)
+ if err != nil {
+ return nil, err
+ }
+ return singlePullRefPairGoal(rp), nil
+}
+// pullGoalFromImageReference returns a pull goal for a single ImageReference, depending on the used transport.
+func (ir *Runtime) pullGoalFromImageReference(ctx context.Context, srcRef types.ImageReference, imgName string, sc *types.SystemContext) (*pullGoal, error) {
// supports pulling from docker-archive, oci, and registries
- if srcRef.Transport().Name() == DockerArchive {
- tarSource, err := tarfile.NewSourceFromFile(archFile)
+ switch srcRef.Transport().Name() {
+ case DockerArchive:
+ archivePath := srcRef.StringWithinTransport()
+ tarSource, err := tarfile.NewSourceFromFile(archivePath)
if err != nil {
return nil, err
}
@@ -112,33 +128,35 @@ func (ir *Runtime) getPullListFromRef(ctx context.Context, srcRef types.ImageRef
if err != nil {
return nil, err
}
- pullInfo, err := ir.getPullRefPair(srcRef, reference)
+ return ir.getSinglePullRefPairGoal(srcRef, reference)
+ }
+
+ if len(manifest[0].RepoTags) == 0 {
+ // If the input image has no repotags, we need to feed it a dest anyways
+ digest, err := getImageDigest(ctx, srcRef, sc)
if err != nil {
return nil, err
}
- pullRefPairs = append(pullRefPairs, pullInfo)
- } else {
- var dest []string
- if len(manifest[0].RepoTags) > 0 {
- dest = append(dest, manifest[0].RepoTags...)
- } else {
- // If the input image has no repotags, we need to feed it a dest anyways
- digest, err := getImageDigest(ctx, srcRef, sc)
- if err != nil {
- return nil, err
- }
- dest = append(dest, digest)
- }
- // Need to load in all the repo tags from the manifest
- for _, dst := range dest {
- pullInfo, err := ir.getPullRefPair(srcRef, dst)
- if err != nil {
- return nil, err
- }
- pullRefPairs = append(pullRefPairs, pullInfo)
+ return ir.getSinglePullRefPairGoal(srcRef, digest)
+ }
+
+ // Need to load in all the repo tags from the manifest
+ res := []pullRefPair{}
+ for _, dst := range manifest[0].RepoTags {
+ pullInfo, err := ir.getPullRefPair(srcRef, dst)
+ if err != nil {
+ return nil, err
}
+ res = append(res, pullInfo)
}
- } else if srcRef.Transport().Name() == OCIArchive {
+ return &pullGoal{
+ refPairs: res,
+ pullAllPairs: true,
+ usedSearchRegistries: false,
+ searchedRegistries: nil,
+ }, nil
+
+ case OCIArchive:
// retrieve the manifest from index.json to access the image name
manifest, err := ociarchive.LoadManifestDescriptor(srcRef)
if err != nil {
@@ -156,55 +174,57 @@ func (ir *Runtime) getPullListFromRef(ctx context.Context, srcRef types.ImageRef
} else {
dest = manifest.Annotations["org.opencontainers.image.ref.name"]
}
- pullInfo, err := ir.getPullRefPair(srcRef, dest)
- if err != nil {
- return nil, err
- }
- pullRefPairs = append(pullRefPairs, pullInfo)
- } else if srcRef.Transport().Name() == DirTransport {
- // supports pull from a directory
- image := splitArr[1]
+ return ir.getSinglePullRefPairGoal(srcRef, dest)
+
+ case DirTransport:
+ path := srcRef.StringWithinTransport()
+ image := path
// remove leading "/"
if image[:1] == "/" {
// Instead of removing the leading /, set localhost as the registry
// so docker.io isn't prepended, and the path becomes the repository
image = DefaultLocalRepo + image
}
- pullInfo, err := ir.getPullRefPair(srcRef, image)
- if err != nil {
- return nil, err
- }
- pullRefPairs = append(pullRefPairs, pullInfo)
- } else {
- pullInfo, err := ir.getPullRefPair(srcRef, imgName)
- if err != nil {
- return nil, err
- }
- pullRefPairs = append(pullRefPairs, pullInfo)
+ return ir.getSinglePullRefPairGoal(srcRef, image)
+
+ default:
+ return ir.getSinglePullRefPairGoal(srcRef, imgName)
}
- return pullRefPairs, nil
}
-// pullImage pulls an image from configured registries
-// By default, only the latest tag (or a specific tag if requested) will be
-// pulled.
-func (i *Image) pullImage(ctx context.Context, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, forceSecure bool) ([]string, error) {
- // pullImage copies the image from the source to the destination
- var pullRefPairs []*pullRefPair
+// pullImageFromHeuristicSource pulls an image based on inputName, which is heuristically parsed and may involve configured registries.
+// Use pullImageFromReference if the source is known precisely.
+func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName string, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, forceSecure bool) ([]string, error) {
+ var goal *pullGoal
sc := GetSystemContext(signaturePolicyPath, authfile, false)
- srcRef, err := alltransports.ParseImageName(i.InputName)
+ srcRef, err := alltransports.ParseImageName(inputName)
if err != nil {
// could be trying to pull from registry with short name
- pullRefPairs, err = i.refPairsFromPossiblyUnqualifiedName()
+ goal, err = ir.pullGoalFromPossiblyUnqualifiedName(inputName)
if err != nil {
return nil, errors.Wrap(err, "error getting default registries to try")
}
} else {
- pullRefPairs, err = i.imageruntime.getPullListFromRef(ctx, srcRef, i.InputName, sc)
+ goal, err = ir.pullGoalFromImageReference(ctx, srcRef, inputName, sc)
if err != nil {
- return nil, errors.Wrapf(err, "error getting pullRefPair info to pull image %q", i.InputName)
+ return nil, errors.Wrapf(err, "error determining pull goal for image %q", inputName)
}
}
+ return ir.doPullImage(ctx, sc, *goal, writer, signingOptions, dockerOptions, forceSecure)
+}
+
+// pullImageFromReference pulls an image from a types.imageReference.
+func (ir *Runtime) pullImageFromReference(ctx context.Context, srcRef types.ImageReference, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, forceSecure bool) ([]string, error) {
+ sc := GetSystemContext(signaturePolicyPath, authfile, false)
+ goal, err := ir.pullGoalFromImageReference(ctx, srcRef, transports.ImageName(srcRef), sc)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error determining pull goal for image %q", transports.ImageName(srcRef))
+ }
+ return ir.doPullImage(ctx, sc, *goal, writer, signingOptions, dockerOptions, forceSecure)
+}
+
+// doPullImage is an internal helper interpreting pullGoal. Almost everyone should call one of the callers of doPullImage instead.
+func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goal pullGoal, writer io.Writer, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, forceSecure bool) ([]string, error) {
policyContext, err := getPolicyContext(sc)
if err != nil {
return nil, err
@@ -216,14 +236,15 @@ func (i *Image) pullImage(ctx context.Context, writer io.Writer, authfile, signa
return nil, err
}
var images []string
- for _, imageInfo := range pullRefPairs {
- copyOptions := getCopyOptions(writer, signaturePolicyPath, dockerOptions, nil, signingOptions, authfile, "", false, nil)
- if strings.HasPrefix(DockerTransport, imageInfo.srcRef.Transport().Name()) {
- imgRef, err := reference.Parse(imageInfo.srcRef.DockerReference().String())
- if err != nil {
- return nil, err
+ for _, imageInfo := range goal.refPairs {
+ copyOptions := getCopyOptions(sc, writer, dockerOptions, nil, signingOptions, "", nil)
+ if imageInfo.srcRef.Transport().Name() == DockerTransport {
+ imgRef := imageInfo.srcRef.DockerReference()
+ if imgRef == nil { // This should never happen; such references can’t be created.
+ return nil, fmt.Errorf("internal error: DockerTransport reference %s does not have a DockerReference",
+ transports.ImageName(imageInfo.srcRef))
}
- registry := reference.Domain(imgRef.(reference.Named))
+ registry := reference.Domain(imgRef)
if util.StringInSlice(registry, insecureRegistries) && !forceSecure {
copyOptions.SourceCtx.DockerInsecureSkipTLSVerify = true
@@ -231,7 +252,7 @@ func (i *Image) pullImage(ctx context.Context, writer io.Writer, authfile, signa
}
}
// Print the following statement only when pulling from a docker or atomic registry
- if writer != nil && (strings.HasPrefix(DockerTransport, imageInfo.srcRef.Transport().Name()) || imageInfo.srcRef.Transport().Name() == AtomicTransport) {
+ if writer != nil && (imageInfo.srcRef.Transport().Name() == DockerTransport || imageInfo.srcRef.Transport().Name() == AtomicTransport) {
io.WriteString(writer, fmt.Sprintf("Trying to pull %s...", imageInfo.image))
}
if err = cp.Image(ctx, policyContext, imageInfo.dstRef, imageInfo.srcRef, copyOptions); err != nil {
@@ -239,7 +260,7 @@ func (i *Image) pullImage(ctx context.Context, writer io.Writer, authfile, signa
io.WriteString(writer, "Failed\n")
}
} else {
- if imageInfo.srcRef.Transport().Name() != DockerArchive {
+ if !goal.pullAllPairs {
return []string{imageInfo.image}, nil
}
images = append(images, imageInfo.image)
@@ -248,15 +269,7 @@ func (i *Image) pullImage(ctx context.Context, writer io.Writer, authfile, signa
// If no image was found, we should handle. Lets be nicer to the user and see if we can figure out why.
if len(images) == 0 {
registryPath := sysregistries.RegistriesConfPath(&types.SystemContext{})
- searchRegistries, err := registries.GetRegistries()
- if err != nil {
- return nil, err
- }
- hasRegistryInName, err := i.hasRegistry()
- if err != nil {
- return nil, err
- }
- if !hasRegistryInName && len(searchRegistries) == 0 {
+ if goal.usedSearchRegistries && len(goal.searchedRegistries) == 0 {
return nil, errors.Errorf("image name provided is a short name and no search registries are defined in %s.", registryPath)
}
return nil, errors.Errorf("unable to find image in the registries defined in %q", registryPath)
@@ -270,19 +283,15 @@ func hasShaInInputName(inputName string) bool {
return strings.Contains(inputName, "@sha256:")
}
-// refNamesFromPossiblyUnqualifiedName looks at a decomposed image and determines the possible
-// image names to try pulling in combination with the registries.conf file as well
-func refNamesFromPossiblyUnqualifiedName(inputName string) ([]*pullRefName, error) {
- var (
- pullNames []*pullRefName
- imageName string
- )
-
+// pullGoalFromPossiblyUnqualifiedName looks at inputName and determines the possible
+// image references to try pulling in combination with the registries.conf file as well
+func (ir *Runtime) pullGoalFromPossiblyUnqualifiedName(inputName string) (*pullGoal, error) {
decomposedImage, err := decompose(inputName)
if err != nil {
return nil, err
}
if decomposedImage.hasRegistry {
+ var imageName, destName string
if hasShaInInputName(inputName) {
imageName = fmt.Sprintf("%s%s", decomposedImage.transport, inputName)
} else {
@@ -292,67 +301,52 @@ func refNamesFromPossiblyUnqualifiedName(inputName string) ([]*pullRefName, erro
if err != nil {
return nil, errors.Wrapf(err, "unable to parse '%s'", inputName)
}
- ps := pullRefName{
- image: inputName,
- srcRef: srcRef,
- }
if hasShaInInputName(inputName) {
- ps.dstName = decomposedImage.assemble()
+ destName = decomposedImage.assemble()
} else {
- ps.dstName = ps.image
+ destName = inputName
}
- pullNames = append(pullNames, &ps)
-
- } else {
- searchRegistries, err := registries.GetRegistries()
+ destRef, err := is.Transport.ParseStoreReference(ir.store, destName)
if err != nil {
- return nil, err
+ return nil, errors.Wrapf(err, "error parsing dest reference name %#v", destName)
}
- for _, registry := range searchRegistries {
- decomposedImage.registry = registry
- imageName := decomposedImage.assembleWithTransport()
- if hasShaInInputName(inputName) {
- imageName = fmt.Sprintf("%s%s/%s", decomposedImage.transport, registry, inputName)
- }
- srcRef, err := alltransports.ParseImageName(imageName)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to parse '%s'", inputName)
- }
- ps := pullRefName{
- image: decomposedImage.assemble(),
- srcRef: srcRef,
- }
- ps.dstName = ps.image
- pullNames = append(pullNames, &ps)
+ ps := pullRefPair{
+ image: inputName,
+ srcRef: srcRef,
+ dstRef: destRef,
}
+ return singlePullRefPairGoal(ps), nil
}
- return pullNames, nil
-}
-// refPairsFromPossiblyUnqualifiedName looks at a decomposed image and determines the possible
-// image references to try pulling in combination with the registries.conf file as well
-func (i *Image) refPairsFromPossiblyUnqualifiedName() ([]*pullRefPair, error) {
- refNames, err := refNamesFromPossiblyUnqualifiedName(i.InputName)
+ searchRegistries, err := registries.GetRegistries()
if err != nil {
return nil, err
}
- return i.imageruntime.pullRefPairsFromRefNames(refNames)
-}
-
-// pullRefPairsFromNames converts a []*pullRefName to []*pullRefPair
-func (ir *Runtime) pullRefPairsFromRefNames(refNames []*pullRefName) ([]*pullRefPair, error) {
- // Here we construct the destination references
- res := make([]*pullRefPair, len(refNames))
- for i, rn := range refNames {
- destRef, err := is.Transport.ParseStoreReference(ir.store, rn.dstName)
+ var refPairs []pullRefPair
+ for _, registry := range searchRegistries {
+ decomposedImage.registry = registry
+ imageName := decomposedImage.assembleWithTransport()
+ if hasShaInInputName(inputName) {
+ imageName = fmt.Sprintf("%s%s/%s", decomposedImage.transport, registry, inputName)
+ }
+ srcRef, err := alltransports.ParseImageName(imageName)
if err != nil {
- return nil, errors.Wrapf(err, "error parsing dest reference name")
+ return nil, errors.Wrapf(err, "unable to parse '%s'", inputName)
}
- res[i] = &pullRefPair{
- image: rn.image,
- srcRef: rn.srcRef,
- dstRef: destRef,
+ ps := pullRefPair{
+ image: decomposedImage.assemble(),
+ srcRef: srcRef,
+ }
+ ps.dstRef, err = is.Transport.ParseStoreReference(ir.store, ps.image)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error parsing dest reference name %#v", ps.image)
}
+ refPairs = append(refPairs, ps)
}
- return res, nil
+ return &pullGoal{
+ refPairs: refPairs,
+ pullAllPairs: false,
+ usedSearchRegistries: true,
+ searchedRegistries: searchRegistries,
+ }, nil
}
diff --git a/libpod/image/pull_test.go b/libpod/image/pull_test.go
index 535dd4e9d..5ef8c47a5 100644
--- a/libpod/image/pull_test.go
+++ b/libpod/image/pull_test.go
@@ -1,24 +1,278 @@
package image
import (
+ "context"
+ "fmt"
"io/ioutil"
"os"
+ "path/filepath"
+ "strings"
"testing"
"github.com/containers/image/transports"
+ "github.com/containers/image/transports/alltransports"
+ "github.com/containers/image/types"
+ "github.com/containers/storage"
+ "github.com/containers/storage/pkg/idtools"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
+// newTestRuntime returns a *Runtime implementation and a cleanup function which the caller is expected to call.
+func newTestRuntime(t *testing.T) (*Runtime, func()) {
+ wd, err := ioutil.TempDir("", "testStorageRuntime")
+ require.NoError(t, err)
+ err = os.MkdirAll(wd, 0700)
+ require.NoError(t, err)
+
+ store, err := storage.GetStore(storage.StoreOptions{
+ RunRoot: filepath.Join(wd, "run"),
+ GraphRoot: filepath.Join(wd, "root"),
+ GraphDriverName: "vfs",
+ GraphDriverOptions: []string{},
+ UIDMap: []idtools.IDMap{{
+ ContainerID: 0,
+ HostID: os.Getuid(),
+ Size: 1,
+ }},
+ GIDMap: []idtools.IDMap{{
+ ContainerID: 0,
+ HostID: os.Getgid(),
+ Size: 1,
+ }},
+ })
+ require.NoError(t, err)
+
+ ir := NewImageRuntimeFromStore(store)
+ cleanup := func() { _ = os.RemoveAll(wd) }
+ return ir, cleanup
+}
+
+// storageReferenceWithoutLocation returns ref.StringWithinTransport(),
+// stripping the [store-specification] prefix from containers/image/storage reference format.
+func storageReferenceWithoutLocation(ref types.ImageReference) string {
+ res := ref.StringWithinTransport()
+ if res[0] == '[' {
+ closeIndex := strings.IndexRune(res, ']')
+ if closeIndex > 0 {
+ res = res[closeIndex+1:]
+ }
+ }
+ return res
+}
+
+func TestGetPullRefPair(t *testing.T) {
+ const imageID = "@0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+ const digestSuffix = "@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+
+ ir, cleanup := newTestRuntime(t)
+ defer cleanup()
+
+ for _, c := range []struct{ srcName, destName, expectedImage, expectedDstName string }{
+ // == Source does not have a Docker reference (as is the case for docker-archive:, oci-archive, dir:); destination formats:
+ { // registry/name, no tag:
+ "dir:/dev/this-does-not-exist", "example.com/from-directory",
+ "example.com/from-directory", "example.com/from-directory:latest",
+ },
+ { // name, no registry, no tag:
+ "dir:/dev/this-does-not-exist", "from-directory",
+ // FIXME(?) Adding a registry also adds a :latest tag. OTOH that actually matches the used destination.
+ // Either way it is surprising that the localhost/ addition changes this. (mitr hoping to remove the "image" member).
+ "localhost/from-directory:latest", "localhost/from-directory:latest",
+ },
+ { // registry/name:tag :
+ "dir:/dev/this-does-not-exist", "example.com/from-directory:notlatest",
+ "example.com/from-directory:notlatest", "example.com/from-directory:notlatest",
+ },
+ { // name:tag, no registry:
+ "dir:/dev/this-does-not-exist", "from-directory:notlatest",
+ "localhost/from-directory:notlatest", "localhost/from-directory:notlatest",
+ },
+ { // name@digest, no registry:
+ "dir:/dev/this-does-not-exist", "from-directory" + digestSuffix,
+ // FIXME?! Why is this dropping the digest, and adding :none?!
+ "localhost/from-directory:none", "localhost/from-directory:none",
+ },
+ { // registry/name@digest:
+ "dir:/dev/this-does-not-exist", "example.com/from-directory" + digestSuffix,
+ "example.com/from-directory" + digestSuffix, "example.com/from-directory" + digestSuffix,
+ },
+ { // ns/name:tag, no registry:
+ // FIXME: This is interpreted as "registry == ns"
+ "dir:/dev/this-does-not-exist", "ns/from-directory:notlatest",
+ "ns/from-directory:notlatest", "docker.io/ns/from-directory:notlatest",
+ },
+ { // containers-storage image ID
+ "dir:/dev/this-does-not-exist", imageID,
+ imageID, imageID,
+ },
+ // == Source does have a Docker reference.
+ // In that case getPullListFromRef uses the full transport:name input as a destName,
+ // which would be invalid in the returned dstName - but dstName is derived from the source, so it does not really matter _so_ much.
+ // Note that unlike real-world use we use different :source and :destination to verify the data flow in more detail.
+ { // registry/name:tag
+ "docker://example.com/busybox:source", "docker://example.com/busybox:destination",
+ "docker://example.com/busybox:destination", "example.com/busybox:source",
+ },
+ { // Implied docker.io/library and :latest
+ "docker://busybox", "docker://busybox:destination",
+ "docker://busybox:destination", "docker.io/library/busybox:latest",
+ },
+ // == Invalid destination format.
+ {"tarball:/dev/null", "tarball:/dev/null", "", ""},
+ } {
+ testDescription := fmt.Sprintf("%#v %#v", c.srcName, c.destName)
+ srcRef, err := alltransports.ParseImageName(c.srcName)
+ require.NoError(t, err, testDescription)
+
+ res, err := ir.getPullRefPair(srcRef, c.destName)
+ if c.expectedDstName == "" {
+ assert.Error(t, err, testDescription)
+ } else {
+ require.NoError(t, err, testDescription)
+ assert.Equal(t, c.expectedImage, res.image, testDescription)
+ assert.Equal(t, srcRef, res.srcRef, testDescription)
+ assert.Equal(t, c.expectedDstName, storageReferenceWithoutLocation(res.dstRef), testDescription)
+ }
+ }
+}
+
+func TestPullGoalFromImageReference(t *testing.T) {
+ ir, cleanup := newTestRuntime(t)
+ defer cleanup()
+
+ type expected struct{ image, dstName string }
+ for _, c := range []struct {
+ srcName string
+ expected []expected
+ expectedPullAllPairs bool
+ }{
+ // == docker-archive:
+ {"docker-archive:/dev/this-does-not-exist", nil, false}, // Input does not exist.
+ {"docker-archive:/dev/null", nil, false}, // Input exists but does not contain a manifest.
+ // FIXME: The implementation has extra code for len(manifest) == 0?! That will fail in getImageDigest anyway.
+ { // RepoTags is empty
+ "docker-archive:testdata/docker-unnamed.tar.xz",
+ []expected{{"@ec9293436c2e66da44edb9efb8d41f6b13baf62283ebe846468bc992d76d7951", "@ec9293436c2e66da44edb9efb8d41f6b13baf62283ebe846468bc992d76d7951"}},
+ false,
+ },
+ { // RepoTags is a [docker.io/library/]name:latest, normalized to the short format.
+ "docker-archive:testdata/docker-name-only.tar.xz",
+ []expected{{"localhost/pretty-empty:latest", "localhost/pretty-empty:latest"}},
+ true,
+ },
+ { // RepoTags is a registry/name:latest
+ "docker-archive:testdata/docker-registry-name.tar.xz",
+ []expected{{"example.com/empty:latest", "example.com/empty:latest"}},
+ true,
+ },
+ { // RepoTags has multiple items for a single image
+ "docker-archive:testdata/docker-two-names.tar.xz",
+ []expected{
+ {"localhost/pretty-empty:latest", "localhost/pretty-empty:latest"},
+ {"example.com/empty:latest", "example.com/empty:latest"},
+ },
+ true,
+ },
+ { // FIXME: Two images in a single archive - only the "first" one (whichever it is) is returned
+ // (and docker-archive: then refuses to read anything when the manifest has more than 1 item)
+ "docker-archive:testdata/docker-two-images.tar.xz",
+ []expected{{"example.com/empty:latest", "example.com/empty:latest"}},
+ // "example.com/empty/but:different" exists but is ignored
+ true,
+ },
+
+ // == oci-archive:
+ {"oci-archive:/dev/this-does-not-exist", nil, false}, // Input does not exist.
+ {"oci-archive:/dev/null", nil, false}, // Input exists but does not contain a manifest.
+ // FIXME: The remaining tests are commented out for now, because oci-archive: does not work unprivileged.
+ // { // No name annotation
+ // "oci-archive:testdata/oci-unnamed.tar.gz",
+ // []expected{{"@5c8aca8137ac47e84c69ae93ce650ce967917cc001ba7aad5494073fac75b8b6", "@5c8aca8137ac47e84c69ae93ce650ce967917cc001ba7aad5494073fac75b8b6"}},
+ // false,
+ // },
+ // { // Name is a name:latest (no normalization is defined).
+ // "oci-archive:testdata/oci-name-only.tar.gz",
+ // []expected{{"localhost/pretty-empty:latest", "localhost/pretty-empty:latest"}},
+ // false,
+ // },
+ // { // Name is a registry/name:latest
+ // "oci-archive:testdata/oci-registry-name.tar.gz",
+ // []expected{{"example.com/empty:latest", "example.com/empty:latest"}},
+ // false,
+ // },
+ // // Name exists, but is an invalid Docker reference; such names will fail when creating dstReference.
+ // {"oci-archive:testdata/oci-non-docker-name.tar.gz", nil, false},
+ // Maybe test support of two images in a single archive? It should be transparently handled by adding a reference to srcRef.
+
+ // == dir:
+ { // Absolute path
+ "dir:/dev/this-does-not-exist",
+ []expected{{"localhost/dev/this-does-not-exist", "localhost/dev/this-does-not-exist:latest"}},
+ false,
+ },
+ { // Relative path, single element.
+ // FIXME? Note the :latest difference in .image.
+ "dir:this-does-not-exist",
+ []expected{{"localhost/this-does-not-exist:latest", "localhost/this-does-not-exist:latest"}},
+ false,
+ },
+ { // Relative path, multiple elements.
+ // FIXME: This does not add localhost/, so dstName is normalized to docker.io/testdata.
+ "dir:testdata/this-does-not-exist",
+ []expected{{"testdata/this-does-not-exist", "docker.io/testdata/this-does-not-exist:latest"}},
+ false,
+ },
+
+ // == Others, notably:
+ // === docker:// (has ImageReference.DockerReference)
+ { // Fully-specified input
+ "docker://docker.io/library/busybox:latest",
+ []expected{{"docker://docker.io/library/busybox:latest", "docker.io/library/busybox:latest"}},
+ false,
+ },
+ { // Minimal form of the input
+ "docker://busybox",
+ []expected{{"docker://busybox", "docker.io/library/busybox:latest"}},
+ false,
+ },
+
+ // === tarball: (as an example of what happens when ImageReference.DockerReference is nil).
+ // FIXME? This tries to parse "tarball:/dev/null" as a storageReference, and fails.
+ // (This is NOT an API promise that the results will continue to be this way.)
+ {"tarball:/dev/null", nil, false},
+ } {
+ srcRef, err := alltransports.ParseImageName(c.srcName)
+ require.NoError(t, err, c.srcName)
+
+ res, err := ir.pullGoalFromImageReference(context.Background(), srcRef, c.srcName, nil)
+ if len(c.expected) == 0 {
+ assert.Error(t, err, c.srcName)
+ } else {
+ require.NoError(t, err, c.srcName)
+ require.Len(t, res.refPairs, len(c.expected), c.srcName)
+ for i, e := range c.expected {
+ testDescription := fmt.Sprintf("%s #%d", c.srcName, i)
+ assert.Equal(t, e.image, res.refPairs[i].image, testDescription)
+ assert.Equal(t, srcRef, res.refPairs[i].srcRef, testDescription)
+ assert.Equal(t, e.dstName, storageReferenceWithoutLocation(res.refPairs[i].dstRef), testDescription)
+ }
+ assert.Equal(t, c.expectedPullAllPairs, res.pullAllPairs, c.srcName)
+ assert.False(t, res.usedSearchRegistries, c.srcName)
+ assert.Nil(t, res.searchedRegistries, c.srcName)
+ }
+ }
+}
+
const registriesConfWithSearch = `[registries.search]
registries = ['example.com', 'docker.io']
`
-func TestRefNamesFromPossiblyUnqualifiedName(t *testing.T) {
+func TestPullGoalFromPossiblyUnqualifiedName(t *testing.T) {
const digestSuffix = "@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
- type pullRefStrings struct{ image, srcRef, dstName string } // pullRefName with string data only
+ type pullRefStrings struct{ image, srcRef, dstName string } // pullRefPair with string data only
- registriesConf, err := ioutil.TempFile("", "TestRefNamesFromPossiblyUnqualifiedName")
+ registriesConf, err := ioutil.TempFile("", "TestPullGoalFromPossiblyUnqualifiedName")
require.NoError(t, err)
defer registriesConf.Close()
defer os.Remove(registriesConf.Name())
@@ -26,6 +280,9 @@ func TestRefNamesFromPossiblyUnqualifiedName(t *testing.T) {
err = ioutil.WriteFile(registriesConf.Name(), []byte(registriesConfWithSearch), 0600)
require.NoError(t, err)
+ ir, cleanup := newTestRuntime(t)
+ defer cleanup()
+
// Environment is per-process, so this looks very unsafe; actually it seems fine because tests are not
// run in parallel unless they opt in by calling t.Parallel(). So don’t do that.
oldRCP, hasRCP := os.LookupEnv("REGISTRIES_CONFIG_PATH")
@@ -39,60 +296,68 @@ func TestRefNamesFromPossiblyUnqualifiedName(t *testing.T) {
os.Setenv("REGISTRIES_CONFIG_PATH", registriesConf.Name())
for _, c := range []struct {
- input string
- expected []pullRefStrings
+ input string
+ expected []pullRefStrings
+ expectedUsedSearchRegistries bool
}{
- {"#", nil}, // Clearly invalid.
+ {"#", nil, false}, // Clearly invalid.
{ // Fully-explicit docker.io, name-only.
"docker.io/library/busybox",
// (The docker:// representation is shortened by c/image/docker.Reference but it refers to "docker.io/library".)
- []pullRefStrings{{"docker.io/library/busybox", "docker://busybox:latest", "docker.io/library/busybox"}},
+ []pullRefStrings{{"docker.io/library/busybox", "docker://busybox:latest", "docker.io/library/busybox:latest"}},
+ false,
},
{ // docker.io with implied /library/, name-only.
"docker.io/busybox",
// (The docker:// representation is shortened by c/image/docker.Reference but it refers to "docker.io/library".)
- // The .dstName fields differ for the explicit/implicit /library/ cases, but StorageTransport.ParseStoreReference normalizes that.
- []pullRefStrings{{"docker.io/busybox", "docker://busybox:latest", "docker.io/busybox"}},
+ []pullRefStrings{{"docker.io/busybox", "docker://busybox:latest", "docker.io/library/busybox:latest"}},
+ false,
},
{ // Qualified example.com, name-only.
"example.com/ns/busybox",
- []pullRefStrings{{"example.com/ns/busybox", "docker://example.com/ns/busybox:latest", "example.com/ns/busybox"}},
+ []pullRefStrings{{"example.com/ns/busybox", "docker://example.com/ns/busybox:latest", "example.com/ns/busybox:latest"}},
+ false,
},
{ // Qualified example.com, name:tag.
"example.com/ns/busybox:notlatest",
[]pullRefStrings{{"example.com/ns/busybox:notlatest", "docker://example.com/ns/busybox:notlatest", "example.com/ns/busybox:notlatest"}},
+ false,
},
{ // Qualified example.com, name@digest.
"example.com/ns/busybox" + digestSuffix,
[]pullRefStrings{{"example.com/ns/busybox" + digestSuffix, "docker://example.com/ns/busybox" + digestSuffix,
// FIXME?! Why is .dstName dropping the digest, and adding :none?!
"example.com/ns/busybox:none"}},
+ false,
},
// Qualified example.com, name:tag@digest. This code is happy to try, but .srcRef parsing currently rejects such input.
- {"example.com/ns/busybox:notlatest" + digestSuffix, nil},
+ {"example.com/ns/busybox:notlatest" + digestSuffix, nil, false},
{ // Unqualified, single-name, name-only
"busybox",
[]pullRefStrings{
{"example.com/busybox:latest", "docker://example.com/busybox:latest", "example.com/busybox:latest"},
// (The docker:// representation is shortened by c/image/docker.Reference but it refers to "docker.io/library".)
- {"docker.io/busybox:latest", "docker://busybox:latest", "docker.io/busybox:latest"},
+ {"docker.io/busybox:latest", "docker://busybox:latest", "docker.io/library/busybox:latest"},
},
+ true,
},
{ // Unqualified, namespaced, name-only
"ns/busybox",
// FIXME: This is interpreted as "registry == ns", and actual pull happens from docker.io/ns/busybox:latest;
// example.com should be first in the list but isn't used at all.
[]pullRefStrings{
- {"ns/busybox", "docker://ns/busybox:latest", "ns/busybox"},
+ {"ns/busybox", "docker://ns/busybox:latest", "docker.io/ns/busybox:latest"},
},
+ false,
},
{ // Unqualified, name:tag
"busybox:notlatest",
[]pullRefStrings{
{"example.com/busybox:notlatest", "docker://example.com/busybox:notlatest", "example.com/busybox:notlatest"},
// (The docker:// representation is shortened by c/image/docker.Reference but it refers to "docker.io/library".)
- {"docker.io/busybox:notlatest", "docker://busybox:notlatest", "docker.io/busybox:notlatest"},
+ {"docker.io/busybox:notlatest", "docker://busybox:notlatest", "docker.io/library/busybox:notlatest"},
},
+ true,
},
{ // Unqualified, name@digest
"busybox" + digestSuffix,
@@ -100,26 +365,31 @@ func TestRefNamesFromPossiblyUnqualifiedName(t *testing.T) {
// FIXME?! Why is .input and .dstName dropping the digest, and adding :none?!
{"example.com/busybox:none", "docker://example.com/busybox" + digestSuffix, "example.com/busybox:none"},
// (The docker:// representation is shortened by c/image/docker.Reference but it refers to "docker.io/library".)
- {"docker.io/busybox:none", "docker://busybox" + digestSuffix, "docker.io/busybox:none"},
+ {"docker.io/busybox:none", "docker://busybox" + digestSuffix, "docker.io/library/busybox:none"},
},
+ true,
},
// Unqualified, name:tag@digest. This code is happy to try, but .srcRef parsing currently rejects such input.
- {"busybox:notlatest" + digestSuffix, nil},
+ {"busybox:notlatest" + digestSuffix, nil, false},
} {
- res, err := refNamesFromPossiblyUnqualifiedName(c.input)
+ res, err := ir.pullGoalFromPossiblyUnqualifiedName(c.input)
if len(c.expected) == 0 {
assert.Error(t, err, c.input)
} else {
assert.NoError(t, err, c.input)
- strings := make([]pullRefStrings, len(res))
- for i, rn := range res {
- strings[i] = pullRefStrings{
- image: rn.image,
- srcRef: transports.ImageName(rn.srcRef),
- dstName: rn.dstName,
- }
+ for i, e := range c.expected {
+ testDescription := fmt.Sprintf("%s #%d", c.input, i)
+ assert.Equal(t, e.image, res.refPairs[i].image, testDescription)
+ assert.Equal(t, e.srcRef, transports.ImageName(res.refPairs[i].srcRef), testDescription)
+ assert.Equal(t, e.dstName, storageReferenceWithoutLocation(res.refPairs[i].dstRef), testDescription)
+ }
+ assert.False(t, res.pullAllPairs, c.input)
+ assert.Equal(t, c.expectedUsedSearchRegistries, res.usedSearchRegistries, c.input)
+ if !c.expectedUsedSearchRegistries {
+ assert.Nil(t, res.searchedRegistries, c.input)
+ } else {
+ assert.Equal(t, []string{"example.com", "docker.io"}, res.searchedRegistries, c.input)
}
- assert.Equal(t, c.expected, strings, c.input)
}
}
}
diff --git a/libpod/image/testdata/docker-name-only.tar.xz b/libpod/image/testdata/docker-name-only.tar.xz
new file mode 100644
index 000000000..0cad9f108
--- /dev/null
+++ b/libpod/image/testdata/docker-name-only.tar.xz
Binary files differ
diff --git a/libpod/image/testdata/docker-registry-name.tar.xz b/libpod/image/testdata/docker-registry-name.tar.xz
new file mode 100644
index 000000000..181816c2e
--- /dev/null
+++ b/libpod/image/testdata/docker-registry-name.tar.xz
Binary files differ
diff --git a/libpod/image/testdata/docker-two-images.tar.xz b/libpod/image/testdata/docker-two-images.tar.xz
new file mode 100644
index 000000000..148d8a86b
--- /dev/null
+++ b/libpod/image/testdata/docker-two-images.tar.xz
Binary files differ
diff --git a/libpod/image/testdata/docker-two-names.tar.xz b/libpod/image/testdata/docker-two-names.tar.xz
new file mode 100644
index 000000000..07fbc479c
--- /dev/null
+++ b/libpod/image/testdata/docker-two-names.tar.xz
Binary files differ
diff --git a/libpod/image/testdata/docker-unnamed.tar.xz b/libpod/image/testdata/docker-unnamed.tar.xz
new file mode 100644
index 000000000..ba6ea1bae
--- /dev/null
+++ b/libpod/image/testdata/docker-unnamed.tar.xz
Binary files differ
diff --git a/libpod/image/testdata/oci-name-only.tar.gz b/libpod/image/testdata/oci-name-only.tar.gz
new file mode 100644
index 000000000..57bc07564
--- /dev/null
+++ b/libpod/image/testdata/oci-name-only.tar.gz
Binary files differ
diff --git a/libpod/image/testdata/oci-non-docker-name.tar.gz b/libpod/image/testdata/oci-non-docker-name.tar.gz
new file mode 100644
index 000000000..5ffc0eabd
--- /dev/null
+++ b/libpod/image/testdata/oci-non-docker-name.tar.gz
Binary files differ
diff --git a/libpod/image/testdata/oci-registry-name.tar.gz b/libpod/image/testdata/oci-registry-name.tar.gz
new file mode 100644
index 000000000..e6df87339
--- /dev/null
+++ b/libpod/image/testdata/oci-registry-name.tar.gz
Binary files differ
diff --git a/libpod/image/testdata/oci-unnamed.tar.gz b/libpod/image/testdata/oci-unnamed.tar.gz
new file mode 100644
index 000000000..de445fdf8
--- /dev/null
+++ b/libpod/image/testdata/oci-unnamed.tar.gz
Binary files differ
diff --git a/libpod/image/utils.go b/libpod/image/utils.go
index de85ca67e..9a75ca6dc 100644
--- a/libpod/image/utils.go
+++ b/libpod/image/utils.go
@@ -42,16 +42,16 @@ func findImageInRepotags(search imageParts, images []*Image) (*storage.Image, er
return results[0], nil
}
-// getCopyOptions constructs a new containers/image/copy.Options{} struct from the given parameters
-func getCopyOptions(reportWriter io.Writer, signaturePolicyPath string, srcDockerRegistry, destDockerRegistry *DockerRegistryOptions, signing SigningOptions, authFile, manifestType string, forceCompress bool, additionalDockerArchiveTags []reference.NamedTagged) *cp.Options {
+// 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(signaturePolicyPath, authFile, forceCompress, additionalDockerArchiveTags)
- destContext := destDockerRegistry.GetSystemContext(signaturePolicyPath, authFile, forceCompress, additionalDockerArchiveTags)
+ srcContext := srcDockerRegistry.GetSystemContext(sc, additionalDockerArchiveTags)
+ destContext := destDockerRegistry.GetSystemContext(sc, additionalDockerArchiveTags)
return &cp.Options{
RemoveSignatures: signing.RemoveSignatures,
SignBy: signing.SignBy,