summaryrefslogtreecommitdiff
path: root/libpod/image/pull.go
diff options
context:
space:
mode:
authorValentin Rothberg <rothberg@redhat.com>2020-07-31 09:27:21 +0200
committerValentin Rothberg <rothberg@redhat.com>2020-09-08 08:47:19 +0200
commit7fea46752cbfb0ef7bfdd694afe95038c9875212 (patch)
treecabd8c0ea232c36cfff7511cb1b1f3bfa30c0bbf /libpod/image/pull.go
parentbe7778df6c70227dab760ea92637ed97dad29641 (diff)
downloadpodman-7fea46752cbfb0ef7bfdd694afe95038c9875212.tar.gz
podman-7fea46752cbfb0ef7bfdd694afe95038c9875212.tar.bz2
podman-7fea46752cbfb0ef7bfdd694afe95038c9875212.zip
support multi-image (docker) archives
Support loading and saving tarballs with more than one image. Add a new `/libpod/images/export` endpoint to the rest API to allow for exporting/saving multiple images into an archive. Note that a non-release version of containers/image is vendored. A release version must be vendored before cutting a new Podman release. We force the containers/image version via a replace in the go.mod file; this way go won't try to match the versions. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
Diffstat (limited to 'libpod/image/pull.go')
-rw-r--r--libpod/image/pull.go113
1 files changed, 70 insertions, 43 deletions
diff --git a/libpod/image/pull.go b/libpod/image/pull.go
index bdcda4016..94d6af4c2 100644
--- a/libpod/image/pull.go
+++ b/libpod/image/pull.go
@@ -11,8 +11,8 @@ import (
cp "github.com/containers/image/v5/copy"
"github.com/containers/image/v5/directory"
"github.com/containers/image/v5/docker"
+ "github.com/containers/image/v5/docker/archive"
dockerarchive "github.com/containers/image/v5/docker/archive"
- "github.com/containers/image/v5/docker/tarfile"
ociarchive "github.com/containers/image/v5/oci/archive"
oci "github.com/containers/image/v5/oci/layout"
is "github.com/containers/image/v5/storage"
@@ -61,12 +61,26 @@ type pullRefPair struct {
dstRef types.ImageReference
}
+// cleanUpFunc is a function prototype for clean-up functions.
+type cleanUpFunc func() error
+
// 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
+ 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
+ cleanUpFuncs []cleanUpFunc // Mainly used to close long-lived objects (e.g., an archive.Reader)
+}
+
+// cleanUp invokes all cleanUpFuncs. Certain resources may not be available
+// anymore. Errors are logged.
+func (p *pullGoal) cleanUp() {
+ for _, f := range p.cleanUpFuncs {
+ if err := f(); err != nil {
+ logrus.Error(err.Error())
+ }
+ }
}
// singlePullRefPairGoal returns a no-frills pull goal for the specified reference pair.
@@ -114,7 +128,49 @@ func (ir *Runtime) getSinglePullRefPairGoal(srcRef types.ImageReference, destNam
return singlePullRefPairGoal(rp), nil
}
+// getPullRefPairsFromDockerArchiveReference returns a slice of pullRefPairs
+// for the specified docker reference and the corresponding archive.Reader.
+func (ir *Runtime) getPullRefPairsFromDockerArchiveReference(ctx context.Context, reader *archive.Reader, ref types.ImageReference, sc *types.SystemContext) ([]pullRefPair, error) {
+ destNames, err := reader.ManifestTagsForReference(ref)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(destNames) == 0 {
+ destName, err := getImageDigest(ctx, ref, sc)
+ if err != nil {
+ return nil, err
+ }
+ destNames = append(destNames, destName)
+ } else {
+ for i := range destNames {
+ ref, err := NormalizedTag(destNames[i])
+ if err != nil {
+ return nil, err
+ }
+ destNames[i] = ref.String()
+ }
+ }
+
+ refPairs := []pullRefPair{}
+ for _, destName := range destNames {
+ destRef, err := is.Transport.ParseStoreReference(ir.store, destName)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error parsing dest reference name %#v", destName)
+ }
+ pair := pullRefPair{
+ image: destName,
+ srcRef: ref,
+ dstRef: destRef,
+ }
+ refPairs = append(refPairs, pair)
+ }
+
+ return refPairs, nil
+}
+
// pullGoalFromImageReference returns a pull goal for a single ImageReference, depending on the used transport.
+// Note that callers are responsible for invoking (*pullGoal).cleanUp() to clean up possibly open resources.
func (ir *Runtime) pullGoalFromImageReference(ctx context.Context, srcRef types.ImageReference, imgName string, sc *types.SystemContext) (*pullGoal, error) {
span, _ := opentracing.StartSpanFromContext(ctx, "pullGoalFromImageReference")
defer span.Finish()
@@ -122,57 +178,26 @@ func (ir *Runtime) pullGoalFromImageReference(ctx context.Context, srcRef types.
// supports pulling from docker-archive, oci, and registries
switch srcRef.Transport().Name() {
case DockerArchive:
- archivePath := srcRef.StringWithinTransport()
- tarSource, err := tarfile.NewSourceFromFile(archivePath)
+ reader, readerRef, err := archive.NewReaderForReference(sc, srcRef)
if err != nil {
return nil, err
}
- defer tarSource.Close()
- manifest, err := tarSource.LoadTarManifest()
+ pairs, err := ir.getPullRefPairsFromDockerArchiveReference(ctx, reader, readerRef, sc)
if err != nil {
- return nil, errors.Wrapf(err, "error retrieving manifest.json")
- }
- // to pull the first image stored in the tar file
- if len(manifest) == 0 {
- // use the hex of the digest if no manifest is found
- reference, err := getImageDigest(ctx, srcRef, sc)
- if err != nil {
- return nil, err
- }
- 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
+ // No need to defer for a single error path.
+ if err := reader.Close(); err != nil {
+ logrus.Error(err.Error())
}
- return ir.getSinglePullRefPairGoal(srcRef, digest)
+ return nil, err
}
- // Need to load in all the repo tags from the manifest
- res := []pullRefPair{}
- for _, dst := range manifest[0].RepoTags {
- //check if image exists and gives a warning of untagging
- localImage, err := ir.NewFromLocal(dst)
- imageID := strings.TrimSuffix(manifest[0].Config, ".json")
- if err == nil && imageID != localImage.ID() {
- logrus.Errorf("the image %s already exists, renaming the old one with ID %s to empty string", dst, localImage.ID())
- }
-
- pullInfo, err := ir.getPullRefPair(srcRef, dst)
- if err != nil {
- return nil, err
- }
- res = append(res, pullInfo)
- }
return &pullGoal{
- refPairs: res,
pullAllPairs: true,
usedSearchRegistries: false,
+ refPairs: pairs,
searchedRegistries: nil,
+ cleanUpFuncs: []cleanUpFunc{reader.Close},
}, nil
case OCIArchive:
@@ -249,6 +274,7 @@ func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName s
return nil, errors.Wrapf(err, "error determining pull goal for image %q", inputName)
}
}
+ defer goal.cleanUp()
return ir.doPullImage(ctx, sc, *goal, writer, signingOptions, dockerOptions, retryOptions, label)
}
@@ -267,6 +293,7 @@ func (ir *Runtime) pullImageFromReference(ctx context.Context, srcRef types.Imag
if err != nil {
return nil, errors.Wrapf(err, "error determining pull goal for image %q", transports.ImageName(srcRef))
}
+ defer goal.cleanUp()
return ir.doPullImage(ctx, sc, *goal, writer, signingOptions, dockerOptions, retryOptions, nil)
}