mirror of
https://github.com/containers/podman.git
synced 2025-11-02 23:39:52 +08:00
migrate Podman to containers/common/libimage
Migrate the Podman code base over to `common/libimage` which replaces `libpod/image` and a lot of glue code entirely. Note that I tried to leave bread crumbs for changed tests. Miscellaneous changes: * Some errors yield different messages which required to alter some tests. * I fixed some pre-existing issues in the code. Others were marked as `//TODO`s to prevent the PR from exploding. * The `NamesHistory` of an image is returned as is from the storage. Previously, we did some filtering which I think is undesirable. Instead we should return the data as stored in the storage. * Touched handlers use the ABI interfaces where possible. * Local image resolution: previously Podman would match "foo" on "myfoo". This behaviour has been changed and Podman will now only match on repository boundaries such that "foo" would match "my/foo" but not "myfoo". I consider the old behaviour to be a bug, at the very least an exotic corner case. * Futhermore, "foo:none" does *not* resolve to a local image "foo" without tag anymore. It's a hill I am (almost) willing to die on. * `image prune` prints the IDs of pruned images. Previously, in some cases, the names were printed instead. The API clearly states ID, so we should stick to it. * Compat endpoint image removal with _force_ deletes the entire not only the specified tag. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
280
vendor/github.com/containers/buildah/pull.go
generated
vendored
280
vendor/github.com/containers/buildah/pull.go
generated
vendored
@ -3,28 +3,16 @@ package buildah
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/buildah/define"
|
||||
"github.com/containers/buildah/pkg/blobcache"
|
||||
"github.com/containers/image/v5/directory"
|
||||
"github.com/containers/image/v5/docker"
|
||||
dockerarchive "github.com/containers/image/v5/docker/archive"
|
||||
"github.com/containers/image/v5/docker/reference"
|
||||
tarfile "github.com/containers/image/v5/docker/tarfile"
|
||||
ociarchive "github.com/containers/image/v5/oci/archive"
|
||||
oci "github.com/containers/image/v5/oci/layout"
|
||||
"github.com/containers/image/v5/signature"
|
||||
is "github.com/containers/image/v5/storage"
|
||||
"github.com/containers/image/v5/transports"
|
||||
"github.com/containers/image/v5/transports/alltransports"
|
||||
"github.com/containers/common/libimage"
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/image/v5/types"
|
||||
encconfig "github.com/containers/ocicrypt/config"
|
||||
"github.com/containers/storage"
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// PullOptions can be used to alter how an image is copied in from somewhere.
|
||||
@ -65,258 +53,44 @@ type PullOptions struct {
|
||||
PullPolicy define.PullPolicy
|
||||
}
|
||||
|
||||
func localImageNameForReference(ctx context.Context, store storage.Store, srcRef types.ImageReference) (string, error) {
|
||||
if srcRef == nil {
|
||||
return "", errors.Errorf("reference to image is empty")
|
||||
}
|
||||
var name string
|
||||
switch srcRef.Transport().Name() {
|
||||
case dockerarchive.Transport.Name():
|
||||
file := srcRef.StringWithinTransport()
|
||||
tarSource, err := tarfile.NewSourceFromFile(file)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error opening tarfile %q as a source image", file)
|
||||
}
|
||||
defer tarSource.Close()
|
||||
manifest, err := tarSource.LoadTarManifest()
|
||||
if err != nil {
|
||||
return "", errors.Errorf("error retrieving manifest.json from tarfile %q: %v", file, err)
|
||||
}
|
||||
// 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
|
||||
name, err = getImageDigest(ctx, srcRef, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
if len(manifest[0].RepoTags) > 0 {
|
||||
name = manifest[0].RepoTags[0]
|
||||
} else {
|
||||
// If the input image has no repotags, we need to feed it a dest anyways
|
||||
name, err = getImageDigest(ctx, srcRef, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
case ociarchive.Transport.Name():
|
||||
// retrieve the manifest from index.json to access the image name
|
||||
manifest, err := ociarchive.LoadManifestDescriptor(srcRef)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error loading manifest for %q", transports.ImageName(srcRef))
|
||||
}
|
||||
// if index.json has no reference name, compute the image digest instead
|
||||
if manifest.Annotations == nil || manifest.Annotations["org.opencontainers.image.ref.name"] == "" {
|
||||
name, err = getImageDigest(ctx, srcRef, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
name = manifest.Annotations["org.opencontainers.image.ref.name"]
|
||||
}
|
||||
case directory.Transport.Name():
|
||||
// supports pull from a directory
|
||||
name = toLocalImageName(srcRef.StringWithinTransport())
|
||||
case oci.Transport.Name():
|
||||
// supports pull from a directory
|
||||
split := strings.SplitN(srcRef.StringWithinTransport(), ":", 2)
|
||||
name = toLocalImageName(split[0])
|
||||
default:
|
||||
ref := srcRef.DockerReference()
|
||||
if ref == nil {
|
||||
name = srcRef.StringWithinTransport()
|
||||
_, err := is.Transport.ParseStoreReference(store, name)
|
||||
if err == nil {
|
||||
return name, nil
|
||||
}
|
||||
logrus.Debugf("error parsing local storage reference %q: %v", name, err)
|
||||
if strings.LastIndex(name, "/") != -1 {
|
||||
name = name[strings.LastIndex(name, "/")+1:]
|
||||
_, err = is.Transport.ParseStoreReference(store, name)
|
||||
if err == nil {
|
||||
return name, errors.Wrapf(err, "error parsing local storage reference %q", name)
|
||||
}
|
||||
}
|
||||
return "", errors.Errorf("reference to image %q is not a named reference", transports.ImageName(srcRef))
|
||||
}
|
||||
|
||||
if named, ok := ref.(reference.Named); ok {
|
||||
name = named.Name()
|
||||
if namedTagged, ok := ref.(reference.NamedTagged); ok {
|
||||
name = name + ":" + namedTagged.Tag()
|
||||
}
|
||||
if canonical, ok := ref.(reference.Canonical); ok {
|
||||
name = name + "@" + canonical.Digest().String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := is.Transport.ParseStoreReference(store, name); err != nil {
|
||||
return "", errors.Wrapf(err, "error parsing computed local image name %q", name)
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// Pull copies the contents of the image from somewhere else to local storage. Returns the
|
||||
// ID of the local image or an error.
|
||||
func Pull(ctx context.Context, imageName string, options PullOptions) (imageID string, err error) {
|
||||
systemContext := getSystemContext(options.Store, options.SystemContext, options.SignaturePolicyPath)
|
||||
libimageOptions := &libimage.PullOptions{}
|
||||
libimageOptions.SignaturePolicyPath = options.SignaturePolicyPath
|
||||
libimageOptions.Writer = options.ReportWriter
|
||||
libimageOptions.RemoveSignatures = options.RemoveSignatures
|
||||
libimageOptions.OciDecryptConfig = options.OciDecryptConfig
|
||||
libimageOptions.AllTags = options.AllTags
|
||||
libimageOptions.RetryDelay = &options.RetryDelay
|
||||
|
||||
boptions := BuilderOptions{
|
||||
FromImage: imageName,
|
||||
SignaturePolicyPath: options.SignaturePolicyPath,
|
||||
SystemContext: systemContext,
|
||||
BlobDirectory: options.BlobDirectory,
|
||||
ReportWriter: options.ReportWriter,
|
||||
MaxPullRetries: options.MaxRetries,
|
||||
PullRetryDelay: options.RetryDelay,
|
||||
OciDecryptConfig: options.OciDecryptConfig,
|
||||
PullPolicy: options.PullPolicy,
|
||||
if options.MaxRetries > 0 {
|
||||
retries := uint(options.MaxRetries)
|
||||
libimageOptions.MaxRetries = &retries
|
||||
}
|
||||
|
||||
if !options.AllTags {
|
||||
_, _, img, err := resolveImage(ctx, systemContext, options.Store, boptions)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return img.ID, nil
|
||||
if options.BlobDirectory != "" {
|
||||
libimageOptions.DestinationLookupReferenceFunc = blobcache.CacheLookupReferenceFunc(options.BlobDirectory, types.PreserveOriginal)
|
||||
}
|
||||
|
||||
srcRef, err := alltransports.ParseImageName(imageName)
|
||||
if err == nil && srcRef.Transport().Name() != docker.Transport.Name() {
|
||||
return "", errors.New("Non-docker transport is not supported, for --all-tags pulling")
|
||||
}
|
||||
|
||||
storageRef, _, _, err := resolveImage(ctx, systemContext, options.Store, boptions)
|
||||
pullPolicy, err := config.ParsePullPolicy(options.PullPolicy.String())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var errs *multierror.Error
|
||||
repo := reference.TrimNamed(storageRef.DockerReference())
|
||||
dockerRef, err := docker.NewReference(reference.TagNameOnly(storageRef.DockerReference()))
|
||||
runtime, err := libimage.RuntimeFromStore(options.Store, &libimage.RuntimeOptions{SystemContext: options.SystemContext})
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "internal error creating docker.Transport reference for %s", storageRef.DockerReference().String())
|
||||
}
|
||||
tags, err := docker.GetRepositoryTags(ctx, systemContext, dockerRef)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error getting repository tags")
|
||||
}
|
||||
for _, tag := range tags {
|
||||
tagged, err := reference.WithTag(repo, tag)
|
||||
if err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
continue
|
||||
}
|
||||
taggedRef, err := docker.NewReference(tagged)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "internal error creating docker.Transport reference for %s", tagged.String())
|
||||
}
|
||||
if options.ReportWriter != nil {
|
||||
if _, err := options.ReportWriter.Write([]byte("Pulling " + tagged.String() + "\n")); err != nil {
|
||||
return "", errors.Wrapf(err, "error writing pull report")
|
||||
}
|
||||
}
|
||||
ref, err := pullImage(ctx, options.Store, taggedRef, options, systemContext)
|
||||
if err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
continue
|
||||
}
|
||||
taggedImg, err := is.Transport.GetStoreImage(options.Store, ref)
|
||||
if err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
continue
|
||||
}
|
||||
imageID = taggedImg.ID
|
||||
return "", err
|
||||
}
|
||||
|
||||
return imageID, errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
func pullImage(ctx context.Context, store storage.Store, srcRef types.ImageReference, options PullOptions, sc *types.SystemContext) (types.ImageReference, error) {
|
||||
blocked, err := isReferenceBlocked(srcRef, sc)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error checking if pulling from registry for %q is blocked", transports.ImageName(srcRef))
|
||||
}
|
||||
if blocked {
|
||||
return nil, errors.Errorf("pull access to registry for %q is blocked by configuration", transports.ImageName(srcRef))
|
||||
}
|
||||
insecure, err := checkRegistrySourcesAllows("pull from", srcRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if insecure {
|
||||
if sc.DockerInsecureSkipTLSVerify == types.OptionalBoolFalse {
|
||||
return nil, errors.Errorf("can't require tls verification on an insecured registry")
|
||||
}
|
||||
sc.DockerInsecureSkipTLSVerify = types.OptionalBoolTrue
|
||||
sc.OCIInsecureSkipTLSVerify = true
|
||||
sc.DockerDaemonInsecureSkipTLSVerify = true
|
||||
}
|
||||
|
||||
destName, err := localImageNameForReference(ctx, store, srcRef)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error computing local image name for %q", transports.ImageName(srcRef))
|
||||
}
|
||||
if destName == "" {
|
||||
return nil, errors.Errorf("error computing local image name for %q", transports.ImageName(srcRef))
|
||||
}
|
||||
|
||||
destRef, err := is.Transport.ParseStoreReference(store, destName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error parsing image name %q", destName)
|
||||
}
|
||||
var maybeCachedDestRef = types.ImageReference(destRef)
|
||||
if options.BlobDirectory != "" {
|
||||
cachedRef, err := blobcache.NewBlobCache(destRef, options.BlobDirectory, types.PreserveOriginal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error wrapping image reference %q in blob cache at %q", transports.ImageName(destRef), options.BlobDirectory)
|
||||
}
|
||||
maybeCachedDestRef = cachedRef
|
||||
}
|
||||
|
||||
policy, err := signature.DefaultPolicy(sc)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error obtaining default signature policy")
|
||||
}
|
||||
|
||||
policyContext, err := signature.NewPolicyContext(policy)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error creating new signature policy context")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err2 := policyContext.Destroy(); err2 != nil {
|
||||
logrus.Debugf("error destroying signature policy context: %v", err2)
|
||||
}
|
||||
}()
|
||||
|
||||
logrus.Debugf("copying %q to %q", transports.ImageName(srcRef), destName)
|
||||
if _, err := retryCopyImage(ctx, policyContext, maybeCachedDestRef, srcRef, srcRef, getCopyOptions(store, options.ReportWriter, sc, sc, "", options.RemoveSignatures, "", nil, nil, options.OciDecryptConfig), options.MaxRetries, options.RetryDelay); err != nil {
|
||||
logrus.Debugf("error copying src image [%q] to dest image [%q] err: %v", transports.ImageName(srcRef), destName, err)
|
||||
return nil, err
|
||||
}
|
||||
return destRef, nil
|
||||
}
|
||||
|
||||
// getImageDigest creates an image object and uses the hex value of the digest as the image ID
|
||||
// for parsing the store reference
|
||||
func getImageDigest(ctx context.Context, src types.ImageReference, sc *types.SystemContext) (string, error) {
|
||||
newImg, err := src.NewImage(ctx, sc)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error opening image %q for reading", transports.ImageName(src))
|
||||
}
|
||||
defer newImg.Close()
|
||||
|
||||
digest := newImg.ConfigInfo().Digest
|
||||
if err = digest.Validate(); err != nil {
|
||||
return "", errors.Wrapf(err, "error getting config info from image %q", transports.ImageName(src))
|
||||
}
|
||||
return "@" + digest.Hex(), nil
|
||||
}
|
||||
|
||||
// toLocalImageName converts an image name into a 'localhost/' prefixed one
|
||||
func toLocalImageName(imageName string) string {
|
||||
return "localhost/" + strings.TrimLeft(imageName, "/")
|
||||
pulledImages, err := runtime.Pull(context.Background(), imageName, pullPolicy, libimageOptions)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(pulledImages) == 0 {
|
||||
return "", errors.Errorf("internal error pulling %s: no image pulled and no error", imageName)
|
||||
}
|
||||
|
||||
return pulledImages[0].ID(), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user