diff options
Diffstat (limited to 'libpod/image/pull.go')
-rw-r--r-- | libpod/image/pull.go | 294 |
1 files changed, 144 insertions, 150 deletions
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 } |