summaryrefslogtreecommitdiff
path: root/pkg/domain/infra/abi
diff options
context:
space:
mode:
authorValentin Rothberg <rothberg@redhat.com>2021-04-22 08:01:12 +0200
committerValentin Rothberg <rothberg@redhat.com>2021-05-05 11:30:12 +0200
commit0f7d54b0260c1be992ee3b9cee359ef3a9e8bd21 (patch)
tree192e52054de2abf0c92d83ecdbc71d498c2ec947 /pkg/domain/infra/abi
parent8eefca5a257121b177562742c972e39e1686140d (diff)
downloadpodman-0f7d54b0260c1be992ee3b9cee359ef3a9e8bd21.tar.gz
podman-0f7d54b0260c1be992ee3b9cee359ef3a9e8bd21.tar.bz2
podman-0f7d54b0260c1be992ee3b9cee359ef3a9e8bd21.zip
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>
Diffstat (limited to 'pkg/domain/infra/abi')
-rw-r--r--pkg/domain/infra/abi/containers.go16
-rw-r--r--pkg/domain/infra/abi/containers_runlabel.go86
-rw-r--r--pkg/domain/infra/abi/images.go582
-rw-r--r--pkg/domain/infra/abi/images_list.go61
-rw-r--r--pkg/domain/infra/abi/manifest.go327
-rw-r--r--pkg/domain/infra/abi/play.go59
-rw-r--r--pkg/domain/infra/abi/system.go21
7 files changed, 550 insertions, 602 deletions
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 82f2a2424..ef3ccab0c 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -15,7 +15,6 @@ import (
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/events"
- "github.com/containers/podman/v3/libpod/image"
"github.com/containers/podman/v3/libpod/logs"
"github.com/containers/podman/v3/pkg/cgroups"
"github.com/containers/podman/v3/pkg/checkpoint"
@@ -23,6 +22,7 @@ import (
"github.com/containers/podman/v3/pkg/domain/entities/reports"
dfilters "github.com/containers/podman/v3/pkg/domain/filters"
"github.com/containers/podman/v3/pkg/domain/infra/abi/terminal"
+ "github.com/containers/podman/v3/pkg/errorhandling"
parallelctr "github.com/containers/podman/v3/pkg/parallel/ctr"
"github.com/containers/podman/v3/pkg/ps"
"github.com/containers/podman/v3/pkg/rootless"
@@ -438,7 +438,8 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string,
default:
return nil, errors.Errorf("unrecognized image format %q", options.Format)
}
- sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
+
+ sc := ic.Libpod.SystemContext()
coptions := buildah.CommitOptions{
SignaturePolicyPath: rtc.Engine.SignaturePolicyPath,
ReportWriter: options.Writer,
@@ -999,14 +1000,9 @@ func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []st
if options.RemoveImage {
_, imageName := ctr.Image()
- ctrImage, err := ic.Libpod.ImageRuntime().NewFromLocal(imageName)
- if err != nil {
- report.RmiErr = err
- reports = append(reports, &report)
- continue
- }
- _, err = ic.Libpod.RemoveImage(ctx, ctrImage, false)
- report.RmiErr = err
+ imageEngine := ImageEngine{Libpod: ic.Libpod}
+ _, rmErrors := imageEngine.Remove(ctx, []string{imageName}, entities.ImageRemoveOptions{})
+ report.RmiErr = errorhandling.JoinErrors(rmErrors)
}
reports = append(reports, &report)
diff --git a/pkg/domain/infra/abi/containers_runlabel.go b/pkg/domain/infra/abi/containers_runlabel.go
index 2cabab988..199ae43ad 100644
--- a/pkg/domain/infra/abi/containers_runlabel.go
+++ b/pkg/domain/infra/abi/containers_runlabel.go
@@ -7,8 +7,9 @@ import (
"path/filepath"
"strings"
+ "github.com/containers/common/libimage"
+ "github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod/define"
- "github.com/containers/podman/v3/libpod/image"
"github.com/containers/podman/v3/pkg/domain/entities"
envLib "github.com/containers/podman/v3/pkg/env"
"github.com/containers/podman/v3/utils"
@@ -18,21 +19,48 @@ import (
)
func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string, imageRef string, args []string, options entities.ContainerRunlabelOptions) error {
- // First, get the image and pull it if needed.
- img, err := ic.runlabelImage(ctx, label, imageRef, options)
+ pullOptions := &libimage.PullOptions{}
+ pullOptions.AuthFilePath = options.Authfile
+ pullOptions.CertDirPath = options.CertDir
+ pullOptions.Credentials = options.Credentials
+ pullOptions.SignaturePolicyPath = options.SignaturePolicy
+ pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify
+
+ pullPolicy := config.PullPolicyNever
+ if options.Pull {
+ pullPolicy = config.PullPolicyMissing
+ }
+ if !options.Quiet {
+ pullOptions.Writer = os.Stderr
+ }
+
+ pulledImages, err := ic.Libpod.LibimageRuntime().Pull(ctx, imageRef, pullPolicy, pullOptions)
if err != nil {
return err
}
+
+ if len(pulledImages) != 1 {
+ return errors.Errorf("internal error: expected an image to be pulled (or an error)")
+ }
+
// Extract the runlabel from the image.
- runlabel, err := img.GetLabel(ctx, label)
+ labels, err := pulledImages[0].Labels(ctx)
if err != nil {
return err
}
+
+ var runlabel string
+ for k, v := range labels {
+ if strings.EqualFold(k, label) {
+ runlabel = v
+ break
+ }
+ }
if runlabel == "" {
return errors.Errorf("cannot find the value of label: %s in image: %s", label, imageRef)
}
- cmd, env, err := generateRunlabelCommand(runlabel, img, args, options)
+ cmd, env, err := generateRunlabelCommand(runlabel, pulledImages[0], imageRef, args, options)
if err != nil {
return err
}
@@ -76,36 +104,9 @@ func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string,
return utils.ExecCmdWithStdStreams(stdIn, stdOut, stdErr, env, cmd[0], cmd[1:]...)
}
-// runlabelImage returns an image based on the specified image AND options.
-func (ic *ContainerEngine) runlabelImage(ctx context.Context, label string, imageRef string, options entities.ContainerRunlabelOptions) (*image.Image, error) {
- // First, look up the image locally. If we get an error and requested
- // to pull, fallthrough and pull it.
- img, err := ic.Libpod.ImageRuntime().NewFromLocal(imageRef)
- switch {
- case err == nil:
- return img, nil
- case !options.Pull:
- return nil, err
- default:
- // Fallthrough and pull!
- }
-
- pullOptions := entities.ImagePullOptions{
- Quiet: options.Quiet,
- CertDir: options.CertDir,
- SkipTLSVerify: options.SkipTLSVerify,
- SignaturePolicy: options.SignaturePolicy,
- Authfile: options.Authfile,
- }
- if _, err := pull(ctx, ic.Libpod.ImageRuntime(), imageRef, pullOptions, &label); err != nil {
- return nil, err
- }
- return ic.Libpod.ImageRuntime().NewFromLocal(imageRef)
-}
-
// generateRunlabelCommand generates the to-be-executed command as a string
// slice along with a base environment.
-func generateRunlabelCommand(runlabel string, img *image.Image, args []string, options entities.ContainerRunlabelOptions) ([]string, []string, error) {
+func generateRunlabelCommand(runlabel string, img *libimage.Image, inputName string, args []string, options entities.ContainerRunlabelOptions) ([]string, []string, error) {
var (
err error
name, imageName string
@@ -113,24 +114,25 @@ func generateRunlabelCommand(runlabel string, img *image.Image, args []string, o
cmd []string
)
- // TODO: How do we get global opts as done in v1?
-
// Extract the imageName (or ID).
- imgNames := img.Names()
+ imgNames := img.NamesHistory()
if len(imgNames) == 0 {
imageName = img.ID()
} else {
+ // The newest name is the first entry in the `NamesHistory`
+ // slice.
imageName = imgNames[0]
}
// Use the user-specified name or extract one from the image.
- if options.Name != "" {
- name = options.Name
- } else {
- name, err = image.GetImageBaseName(imageName)
- if err != nil {
- return nil, nil, err
+ name = options.Name
+ if name == "" {
+ normalize := imageName
+ if !strings.HasPrefix(img.ID(), inputName) {
+ normalize = inputName
}
+ splitImageName := strings.Split(normalize, "/")
+ name = splitImageName[len(splitImageName)-1]
}
// Append the user-specified arguments to the runlabel (command).
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 84c7ebecd..0364b00a3 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -3,15 +3,14 @@ package abi
import (
"context"
"fmt"
- "io"
"io/ioutil"
"net/url"
"os"
"path"
"path/filepath"
"strconv"
- "strings"
+ "github.com/containers/common/libimage"
"github.com/containers/common/pkg/config"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/docker/reference"
@@ -19,14 +18,11 @@ import (
"github.com/containers/image/v5/signature"
"github.com/containers/image/v5/transports"
"github.com/containers/image/v5/transports/alltransports"
- "github.com/containers/image/v5/types"
- "github.com/containers/podman/v3/libpod/define"
- "github.com/containers/podman/v3/libpod/image"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/domain/entities/reports"
domainUtils "github.com/containers/podman/v3/pkg/domain/utils"
+ "github.com/containers/podman/v3/pkg/errorhandling"
"github.com/containers/podman/v3/pkg/rootless"
- "github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage"
dockerRef "github.com/docker/distribution/reference"
"github.com/opencontainers/go-digest"
@@ -36,31 +32,84 @@ import (
)
func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.BoolReport, error) {
- _, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
+ exists, err := ir.Libpod.LibimageRuntime().Exists(nameOrID)
if err != nil {
- if errors.Cause(err) == define.ErrMultipleImages {
- return &entities.BoolReport{Value: true}, nil
- }
- if errors.Cause(err) != define.ErrNoSuchImage {
- return nil, err
- }
+ return nil, err
}
- return &entities.BoolReport{Value: err == nil}, nil
+ return &entities.BoolReport{Value: exists}, nil
}
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) ([]*reports.PruneReport, error) {
- reports, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter)
- if err != nil {
- return nil, err
+ // NOTE: the terms "dangling" and "intermediate" are not used
+ // consistently across our code base. In libimage, "dangling" means
+ // that an image has no tags. "intermediate" means that an image is
+ // dangling and that no other image depends on it (i.e., has no
+ // children).
+ //
+ // While pruning usually refers to "dangling" images, it has always
+ // removed "intermediate" ones.
+ defaultOptions := &libimage.RemoveImagesOptions{
+ Filters: append(opts.Filter, "intermediate=true", "containers=false", "readonly=false"),
+ WithSize: true,
+ }
+
+ // `image prune --all` means to *also* remove images which are not in
+ // use by any container. Since image filters are chained, we need to
+ // do two look ups since the default ones are a subset of all.
+ unusedOptions := &libimage.RemoveImagesOptions{
+ Filters: append(opts.Filter, "containers=false", "readonly=false"),
+ WithSize: true,
+ }
+
+ var pruneReports []*reports.PruneReport
+
+ // Now prune all images until we converge.
+ numPreviouslyRemovedImages := 1
+ for {
+ removedDefault, rmErrors := ir.Libpod.LibimageRuntime().RemoveImages(ctx, nil, defaultOptions)
+ if rmErrors != nil {
+ return nil, errorhandling.JoinErrors(rmErrors)
+ }
+ removedUnused, rmErrors := ir.Libpod.LibimageRuntime().RemoveImages(ctx, nil, unusedOptions)
+ if rmErrors != nil {
+ return nil, errorhandling.JoinErrors(rmErrors)
+ }
+
+ for _, rmReport := range append(removedDefault, removedUnused...) {
+ r := *rmReport
+ pruneReports = append(pruneReports, &reports.PruneReport{
+ Id: r.ID,
+ Size: uint64(r.Size),
+ })
+ }
+
+ numRemovedImages := len(removedDefault) + len(removedUnused)
+ if numRemovedImages+numPreviouslyRemovedImages == 0 {
+ break
+ }
+ numPreviouslyRemovedImages = numRemovedImages
}
- return reports, err
+
+ return pruneReports, nil
+}
+
+func toDomainHistoryLayer(layer *libimage.ImageHistory) entities.ImageHistoryLayer {
+ l := entities.ImageHistoryLayer{}
+ l.ID = layer.ID
+ l.Created = *layer.Created
+ l.CreatedBy = layer.CreatedBy
+ copy(l.Tags, layer.Tags)
+ l.Size = layer.Size
+ l.Comment = layer.Comment
+ return l
}
func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) {
- image, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
+ image, _, err := ir.Libpod.LibimageRuntime().LookupImage(nameOrID, &libimage.LookupImageOptions{IgnorePlatform: true})
if err != nil {
return nil, err
}
+
results, err := image.History(ctx)
if err != nil {
return nil, err
@@ -70,17 +119,17 @@ func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entiti
Layers: make([]entities.ImageHistoryLayer, len(results)),
}
- for i, layer := range results {
- history.Layers[i] = ToDomainHistoryLayer(layer)
+ for i := range results {
+ history.Layers[i] = toDomainHistoryLayer(&results[i])
}
return &history, nil
}
func (ir *ImageEngine) Mount(ctx context.Context, nameOrIDs []string, opts entities.ImageMountOptions) ([]*entities.ImageMountReport, error) {
- var (
- images []*image.Image
- err error
- )
+ if opts.All && len(nameOrIDs) > 0 {
+ return nil, errors.Errorf("cannot mix --all with images")
+ }
+
if os.Geteuid() != 0 {
if driver := ir.Libpod.StorageConfig().GraphDriverName; driver != "vfs" {
// Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part
@@ -96,219 +145,129 @@ func (ir *ImageEngine) Mount(ctx context.Context, nameOrIDs []string, opts entit
os.Exit(ret)
}
}
+
+ listImagesOptions := &libimage.ListImagesOptions{}
if opts.All {
- allImages, err := ir.Libpod.ImageRuntime().GetImages()
- if err != nil {
- return nil, err
- }
- for _, img := range allImages {
- if !img.IsReadOnly() {
- images = append(images, img)
- }
- }
- } else {
- for _, i := range nameOrIDs {
- img, err := ir.Libpod.ImageRuntime().NewFromLocal(i)
- if err != nil {
- return nil, err
- }
- images = append(images, img)
- }
+ listImagesOptions.Filters = []string{"readonly=false"}
}
- reports := make([]*entities.ImageMountReport, 0, len(images))
- for _, img := range images {
- report := entities.ImageMountReport{Id: img.ID()}
- if img.IsReadOnly() {
- report.Err = errors.Errorf("mounting readonly %s image not supported", img.ID())
- } else {
- report.Path, report.Err = img.Mount([]string{}, "")
- }
- reports = append(reports, &report)
- }
- if len(reports) > 0 {
- return reports, nil
- }
-
- images, err = ir.Libpod.ImageRuntime().GetImages()
+ images, err := ir.Libpod.LibimageRuntime().ListImages(ctx, nameOrIDs, listImagesOptions)
if err != nil {
return nil, err
}
+
+ mountReports := []*entities.ImageMountReport{}
+ listMountsOnly := !opts.All && len(nameOrIDs) == 0
for _, i := range images {
- mounted, path, err := i.Mounted()
- if err != nil {
- if errors.Cause(err) == storage.ErrLayerUnknown {
- continue
- }
- return nil, err
- }
- if mounted {
- tags, err := i.RepoTags()
+ // TODO: the .Err fields are not used. This pre-dates the
+ // libimage migration but should be addressed at some point.
+ // A quick glimpse at cmd/podman/image/mount.go suggests that
+ // the errors needed to be handled there as well.
+ var mountPoint string
+ var err error
+ if listMountsOnly {
+ // We're only looking for mounted images.
+ mountPoint, err = i.Mountpoint()
if err != nil {
return nil, err
}
- reports = append(reports, &entities.ImageMountReport{
- Id: i.ID(),
- Name: string(i.Digest()),
- Repositories: tags,
- Path: path,
- })
- }
- }
- return reports, nil
-}
-
-func (ir *ImageEngine) Unmount(ctx context.Context, nameOrIDs []string, options entities.ImageUnmountOptions) ([]*entities.ImageUnmountReport, error) {
- var images []*image.Image
-
- if options.All {
- allImages, err := ir.Libpod.ImageRuntime().GetImages()
- if err != nil {
- return nil, err
- }
- for _, img := range allImages {
- if !img.IsReadOnly() {
- images = append(images, img)
+ // Not mounted, so skip.
+ if mountPoint == "" {
+ continue
}
- }
- } else {
- for _, i := range nameOrIDs {
- img, err := ir.Libpod.ImageRuntime().NewFromLocal(i)
+ } else {
+ mountPoint, err = i.Mount(ctx, nil, "")
if err != nil {
return nil, err
}
- images = append(images, img)
}
- }
- reports := []*entities.ImageUnmountReport{}
- for _, img := range images {
- report := entities.ImageUnmountReport{Id: img.ID()}
- mounted, _, err := img.Mounted()
+ tags, err := i.RepoTags()
if err != nil {
- // Errors will be caught in Unmount call below
- // Default assumption to mounted
- mounted = true
- }
- if !mounted {
- continue
- }
- if err := img.Unmount(options.Force); err != nil {
- if options.All && errors.Cause(err) == storage.ErrLayerNotMounted {
- logrus.Debugf("Error umounting image %s, storage.ErrLayerNotMounted", img.ID())
- continue
- }
- report.Err = errors.Wrapf(err, "error unmounting image %s", img.ID())
+ return nil, err
}
- reports = append(reports, &report)
+ mountReports = append(mountReports, &entities.ImageMountReport{
+ Id: i.ID(),
+ Name: string(i.Digest()),
+ Repositories: tags,
+ Path: mountPoint,
+ })
}
- return reports, nil
+ return mountReports, nil
}
-func ToDomainHistoryLayer(layer *image.History) entities.ImageHistoryLayer {
- l := entities.ImageHistoryLayer{}
- l.ID = layer.ID
- l.Created = *layer.Created
- l.CreatedBy = layer.CreatedBy
- copy(l.Tags, layer.Tags)
- l.Size = layer.Size
- l.Comment = layer.Comment
- return l
-}
-
-func pull(ctx context.Context, runtime *image.Runtime, rawImage string, options entities.ImagePullOptions, label *string) (*entities.ImagePullReport, error) {
- var writer io.Writer
- if !options.Quiet {
- writer = os.Stderr
- }
-
- dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
- imageRef, err := alltransports.ParseImageName(rawImage)
- if err != nil {
- imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, rawImage))
- if err != nil {
- return nil, errors.Wrapf(err, "invalid image reference %q", rawImage)
- }
+func (ir *ImageEngine) Unmount(ctx context.Context, nameOrIDs []string, options entities.ImageUnmountOptions) ([]*entities.ImageUnmountReport, error) {
+ if options.All && len(nameOrIDs) > 0 {
+ return nil, errors.Errorf("cannot mix --all with images")
}
- var registryCreds *types.DockerAuthConfig
- if len(options.Username) > 0 && len(options.Password) > 0 {
- registryCreds = &types.DockerAuthConfig{
- Username: options.Username,
- Password: options.Password,
- }
+ listImagesOptions := &libimage.ListImagesOptions{}
+ if options.All {
+ listImagesOptions.Filters = []string{"readonly=false"}
}
- dockerRegistryOptions := image.DockerRegistryOptions{
- DockerRegistryCreds: registryCreds,
- DockerCertPath: options.CertDir,
- OSChoice: options.OS,
- ArchitectureChoice: options.Arch,
- VariantChoice: options.Variant,
- DockerInsecureSkipTLSVerify: options.SkipTLSVerify,
+ images, err := ir.Libpod.LibimageRuntime().ListImages(ctx, nameOrIDs, listImagesOptions)
+ if err != nil {
+ return nil, err
}
- if !options.AllTags {
- newImage, err := runtime.New(ctx, rawImage, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, label, options.PullPolicy, nil)
+ unmountReports := []*entities.ImageUnmountReport{}
+ for _, image := range images {
+ r := &entities.ImageUnmountReport{Id: image.ID()}
+ mountPoint, err := image.Mountpoint()
if err != nil {
- return nil, err
+ r.Err = err
+ unmountReports = append(unmountReports, r)
+ continue
}
- return &entities.ImagePullReport{Images: []string{newImage.ID()}}, nil
- }
-
- // --all-tags requires the docker transport
- if imageRef.Transport().Name() != docker.Transport.Name() {
- return nil, errors.New("--all-tags requires docker transport")
+ if mountPoint == "" {
+ // Skip if the image wasn't mounted.
+ continue
+ }
+ r.Err = image.Unmount(options.Force)
+ unmountReports = append(unmountReports, r)
}
+ return unmountReports, nil
+}
- // Trim the docker-transport prefix.
- rawImage = strings.TrimPrefix(rawImage, docker.Transport.Name())
+func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) (*entities.ImagePullReport, error) {
+ pullOptions := &libimage.PullOptions{AllTags: options.AllTags}
+ pullOptions.AuthFilePath = options.Authfile
+ pullOptions.CertDirPath = options.CertDir
+ pullOptions.Username = options.Username
+ pullOptions.Password = options.Password
+ pullOptions.Architecture = options.Arch
+ pullOptions.OS = options.OS
+ pullOptions.Variant = options.Variant
+ pullOptions.SignaturePolicyPath = options.SignaturePolicy
+ pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify
- // all-tags doesn't work with a tagged reference, so let's check early
- namedRef, err := reference.Parse(rawImage)
- if err != nil {
- return nil, errors.Wrapf(err, "error parsing %q", rawImage)
- }
- if _, isTagged := namedRef.(reference.Tagged); isTagged {
- return nil, errors.New("--all-tags requires a reference without a tag")
+ if !options.Quiet {
+ pullOptions.Writer = os.Stderr
}
- systemContext := image.GetSystemContext("", options.Authfile, false)
- tags, err := docker.GetRepositoryTags(ctx, systemContext, imageRef)
+ pulledImages, err := ir.Libpod.LibimageRuntime().Pull(ctx, rawImage, options.PullPolicy, pullOptions)
if err != nil {
- return nil, errors.Wrapf(err, "error getting repository tags")
+ return nil, err
}
- foundIDs := []string{}
- for _, tag := range tags {
- name := rawImage + ":" + tag
- newImage, err := runtime.New(ctx, name, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways, nil)
- if err != nil {
- logrus.Errorf("error pulling image %q", name)
- continue
- }
- foundIDs = append(foundIDs, newImage.ID())
+ pulledIDs := make([]string, len(pulledImages))
+ for i := range pulledImages {
+ pulledIDs[i] = pulledImages[i].ID()
}
- if len(tags) != len(foundIDs) {
- return nil, err
- }
- return &entities.ImagePullReport{Images: foundIDs}, nil
-}
-
-func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) (*entities.ImagePullReport, error) {
- return pull(ctx, ir.Libpod.ImageRuntime(), rawImage, options, nil)
+ return &entities.ImagePullReport{Images: pulledIDs}, nil
}
func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts entities.InspectOptions) ([]*entities.ImageInspectReport, []error, error) {
reports := []*entities.ImageInspectReport{}
errs := []error{}
for _, i := range namesOrIDs {
- img, err := ir.Libpod.ImageRuntime().NewFromLocal(i)
+ img, _, err := ir.Libpod.LibimageRuntime().LookupImage(i, &libimage.LookupImageOptions{IgnorePlatform: true})
if err != nil {
// This is probably a no such image, treat as nonfatal.
errs = append(errs, err)
continue
}
- result, err := img.Inspect(ctx)
+ result, err := img.Inspect(ctx, true)
if err != nil {
// This is more likely to be fatal.
return nil, nil, err
@@ -323,11 +282,6 @@ func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts en
}
func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error {
- var writer io.Writer
- if !options.Quiet {
- writer = os.Stderr
- }
-
var manifestType string
switch options.Format {
case "":
@@ -342,58 +296,56 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri
return errors.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", options.Format)
}
- var registryCreds *types.DockerAuthConfig
- if len(options.Username) > 0 && len(options.Password) > 0 {
- registryCreds = &types.DockerAuthConfig{
- Username: options.Username,
- Password: options.Password,
- }
- }
- dockerRegistryOptions := image.DockerRegistryOptions{
- DockerRegistryCreds: registryCreds,
- DockerCertPath: options.CertDir,
- DockerInsecureSkipTLSVerify: options.SkipTLSVerify,
- }
+ pushOptions := &libimage.PushOptions{}
+ pushOptions.AuthFilePath = options.Authfile
+ pushOptions.CertDirPath = options.CertDir
+ pushOptions.DirForceCompress = options.Compress
+ pushOptions.Username = options.Username
+ pushOptions.Password = options.Password
+ pushOptions.ManifestMIMEType = manifestType
+ pushOptions.RemoveSignatures = options.RemoveSignatures
+ pushOptions.SignBy = options.SignBy
+ pushOptions.InsecureSkipTLSVerify = options.SkipTLSVerify
- signOptions := image.SigningOptions{
- RemoveSignatures: options.RemoveSignatures,
- SignBy: options.SignBy,
+ if !options.Quiet {
+ pushOptions.Writer = os.Stderr
}
- newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(source)
- if err != nil {
- return err
- }
+ pushedManifestBytes, pushError := ir.Libpod.LibimageRuntime().Push(ctx, source, destination, pushOptions)
+ if pushError == nil {
+ if options.DigestFile != "" {
+ manifestDigest, err := manifest.Digest(pushedManifestBytes)
+ if err != nil {
+ return err
+ }
- err = newImage.PushImageToHeuristicDestination(
- ctx,
- destination,
- manifestType,
- options.Authfile,
- options.DigestFile,
- options.SignaturePolicy,
- writer,
- options.Compress,
- signOptions,
- &dockerRegistryOptions,
- nil,
- options.Progress)
- if err != nil && errors.Cause(err) != storage.ErrImageUnknown {
+ if err := ioutil.WriteFile(options.DigestFile, []byte(manifestDigest.String()), 0644); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+ // If the image could not be found, we may be referring to a manifest
+ // list but could not find a matching image instance in the local
+ // containers storage. In that case, fall back and attempt to push the
+ // (entire) manifest.
+ if errors.Cause(pushError) == storage.ErrImageUnknown {
// Image might be a manifest list so attempt a manifest push
- if _, manifestErr := ir.ManifestPush(ctx, source, destination, options); manifestErr == nil {
+ _, manifestErr := ir.ManifestPush(ctx, source, destination, options)
+ if manifestErr == nil {
return nil
}
}
- return err
+ return pushError
}
func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, options entities.ImageTagOptions) error {
- newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
+ image, _, err := ir.Libpod.LibimageRuntime().LookupImage(nameOrID, &libimage.LookupImageOptions{IgnorePlatform: true})
if err != nil {
return err
}
for _, tag := range tags {
- if err := newImage.TagImage(tag); err != nil {
+ if err := image.Tag(tag); err != nil {
return err
}
}
@@ -401,54 +353,71 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string,
}
func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, options entities.ImageUntagOptions) error {
- newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
+ image, _, err := ir.Libpod.LibimageRuntime().LookupImage(nameOrID, &libimage.LookupImageOptions{IgnorePlatform: true})
if err != nil {
return err
}
// If only one arg is provided, all names are to be untagged
if len(tags) == 0 {
- tags = newImage.Names()
+ tags = image.Names()
}
for _, tag := range tags {
- if err := newImage.UntagImage(tag); err != nil {
+ if err := image.Untag(tag); err != nil {
return err
}
}
return nil
}
-func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) (*entities.ImageLoadReport, error) {
- var (
- writer io.Writer
- )
- if !opts.Quiet {
- writer = os.Stderr
- }
- name, err := ir.Libpod.LoadImage(ctx, opts.Input, writer, opts.SignaturePolicy)
- if err != nil {
- return nil, err
+func (ir *ImageEngine) Load(ctx context.Context, options entities.ImageLoadOptions) (*entities.ImageLoadReport, error) {
+ loadOptions := &libimage.LoadOptions{}
+ loadOptions.SignaturePolicyPath = options.SignaturePolicy
+ if !options.Quiet {
+ loadOptions.Writer = os.Stderr
}
- return &entities.ImageLoadReport{Names: strings.Split(name, ",")}, nil
-}
-func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) {
- id, err := ir.Libpod.Import(ctx, opts.Source, opts.Reference, opts.SignaturePolicy, opts.Changes, opts.Message, opts.Quiet)
+ loadedImages, err := ir.Libpod.LibimageRuntime().Load(ctx, options.Input, loadOptions)
if err != nil {
return nil, err
}
- return &entities.ImageImportReport{Id: id}, nil
+ return &entities.ImageLoadReport{Names: loadedImages}, nil
}
func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error {
+ saveOptions := &libimage.SaveOptions{}
+ saveOptions.DirForceCompress = options.Compress
+ saveOptions.RemoveSignatures = options.RemoveSignatures
+
+ if !options.Quiet {
+ saveOptions.Writer = os.Stderr
+ }
+
+ names := []string{nameOrID}
if options.MultiImageArchive {
- nameOrIDs := append([]string{nameOrID}, tags...)
- return ir.Libpod.ImageRuntime().SaveImages(ctx, nameOrIDs, options.Format, options.Output, options.Quiet, true)
+ names = append(names, tags...)
+ } else {
+ saveOptions.AdditionalTags = tags
}
- newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
+ return ir.Libpod.LibimageRuntime().Save(ctx, names, options.Format, options.Output, saveOptions)
+}
+
+func (ir *ImageEngine) Import(ctx context.Context, options entities.ImageImportOptions) (*entities.ImageImportReport, error) {
+ importOptions := &libimage.ImportOptions{}
+ importOptions.Changes = options.Changes
+ importOptions.CommitMessage = options.Message
+ importOptions.Tag = options.Reference
+ importOptions.SignaturePolicyPath = options.SignaturePolicy
+
+ if !options.Quiet {
+ importOptions.Writer = os.Stderr
+ }
+
+ imageID, err := ir.Libpod.LibimageRuntime().Import(ctx, options.Source, importOptions)
if err != nil {
- return err
+ return nil, err
}
- return newImage.Save(ctx, nameOrID, options.Format, options.Output, tags, options.Quiet, options.Compress, true)
+
+ return &entities.ImageImportReport{Id: imageID}, nil
}
func (ir *ImageEngine) Diff(_ context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) {
@@ -460,12 +429,12 @@ func (ir *ImageEngine) Diff(_ context.Context, nameOrID string, _ entities.DiffO
}
func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) {
- filter, err := image.ParseSearchFilter(opts.Filters)
+ filter, err := libimage.ParseSearchFilter(opts.Filters)
if err != nil {
return nil, err
}
- searchOpts := image.SearchOptions{
+ searchOptions := &libimage.SearchOptions{
Authfile: opts.Authfile,
Filter: *filter,
Limit: opts.Limit,
@@ -474,7 +443,7 @@ func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.Im
ListTags: opts.ListTags,
}
- searchResults, err := image.SearchImages(term, searchOpts)
+ searchResults, err := ir.Libpod.LibimageRuntime().Search(ctx, term, searchOptions)
if err != nil {
return nil, err
}
@@ -510,15 +479,15 @@ func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts
}
func (ir *ImageEngine) Tree(ctx context.Context, nameOrID string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) {
- img, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
+ image, _, err := ir.Libpod.LibimageRuntime().LookupImage(nameOrID, &libimage.LookupImageOptions{IgnorePlatform: true})
if err != nil {
return nil, err
}
- results, err := img.GenerateTree(opts.WhatRequires)
+ tree, err := image.Tree(opts.WhatRequires)
if err != nil {
return nil, err
}
- return &entities.ImageTreeReport{Tree: results}, nil
+ return &entities.ImageTreeReport{Tree: tree}, nil
}
// removeErrorsToExitCode returns an exit code for the specified slice of
@@ -542,9 +511,9 @@ func removeErrorsToExitCode(rmErrors []error) int {
for _, e := range rmErrors {
switch errors.Cause(e) {
- case define.ErrNoSuchImage:
+ case storage.ErrImageUnknown, storage.ErrLayerUnknown:
noSuchImageErrors = true
- case define.ErrImageInUse, storage.ErrImageUsedByContainer:
+ case storage.ErrImageUsedByContainer:
inUseErrors = true
default:
otherErrors = true
@@ -574,84 +543,25 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
report.ExitCode = removeErrorsToExitCode(rmErrors)
}()
- // deleteImage is an anonymous function to conveniently delete an image
- // without having to pass all local data around.
- deleteImage := func(img *image.Image) error {
- results, err := ir.Libpod.RemoveImage(ctx, img, opts.Force)
- switch errors.Cause(err) {
- case nil:
- // Removal worked, so let's report it.
- report.Deleted = append(report.Deleted, results.Deleted)
- report.Untagged = append(report.Untagged, results.Untagged...)
- return nil
- case storage.ErrImageUnknown, storage.ErrLayerUnknown:
- // The image must have been removed already (see #6510)
- // or the storage is corrupted (see #9617).
- report.Deleted = append(report.Deleted, img.ID())
- report.Untagged = append(report.Untagged, img.ID())
- return nil
- default:
- // Fatal error.
- return err
- }
+ libimageOptions := &libimage.RemoveImagesOptions{}
+ libimageOptions.Filters = []string{"readonly=false"}
+ libimageOptions.Force = opts.Force
+ if !opts.All {
+ libimageOptions.Filters = append(libimageOptions.Filters, "intermediate=false")
}
+ libimageOptions.RemoveContainerFunc = ir.Libpod.RemoveContainersForImageCallback(ctx)
- // Delete all images from the local storage.
- if opts.All {
- previousImages := 0
- // Remove all images one-by-one.
- for {
- storageImages, err := ir.Libpod.ImageRuntime().GetRWImages()
- if err != nil {
- rmErrors = append(rmErrors, err)
- return
- }
- // No images (left) to remove, so we're done.
- if len(storageImages) == 0 {
- return
- }
- // Prevent infinity loops by making a delete-progress check.
- if previousImages == len(storageImages) {
- rmErrors = append(rmErrors, errors.New("unable to delete all images, check errors and re-run image removal if needed"))
- break
- }
- previousImages = len(storageImages)
- // Delete all "leaves" (i.e., images without child images).
- for _, img := range storageImages {
- isParent, err := img.IsParent(ctx)
- if err != nil {
- logrus.Warnf("%v, ignoring the error", err)
- isParent = false
- }
- // Skip parent images.
- if isParent {
- continue
- }
- if err := deleteImage(img); err != nil {
- rmErrors = append(rmErrors, err)
- }
- }
- }
-
- return
- }
+ libimageReport, libimageErrors := ir.Libpod.LibimageRuntime().RemoveImages(ctx, images, libimageOptions)
- // Delete only the specified images.
- for _, id := range images {
- img, err := ir.Libpod.ImageRuntime().NewFromLocal(id)
- if err != nil {
- // attempt to remove image from storage
- if forceErr := ir.Libpod.RemoveImageFromStorage(id); forceErr == nil {
- continue
- }
- rmErrors = append(rmErrors, err)
- continue
- }
- err = deleteImage(img)
- if err != nil {
- rmErrors = append(rmErrors, err)
+ for _, r := range libimageReport {
+ if r.Removed {
+ report.Deleted = append(report.Deleted, r.ID)
}
+ report.Untagged = append(report.Untagged, r.Untagged...)
}
+
+ rmErrors = libimageErrors
+
return //nolint
}
diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go
index 3b8aabeb7..b0e947991 100644
--- a/pkg/domain/infra/abi/images_list.go
+++ b/pkg/domain/infra/abi/images_list.go
@@ -3,23 +3,25 @@ package abi
import (
"context"
- libpodImage "github.com/containers/podman/v3/libpod/image"
+ "github.com/containers/common/libimage"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/pkg/errors"
)
func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) {
- images, err := ir.Libpod.ImageRuntime().GetImagesWithFilters(opts.Filter)
- if err != nil {
- return nil, err
+ listImagesOptions := &libimage.ListImagesOptions{
+ Filters: opts.Filter,
}
-
if !opts.All {
- filter, err := ir.Libpod.ImageRuntime().IntermediateFilter(ctx, images)
- if err != nil {
- return nil, err
- }
- images = libpodImage.FilterImages(images, []libpodImage.ResultFilter{filter})
+ // Filter intermediate images unless we want to list *all*.
+ // NOTE: it's a positive filter, so `intermediate=false` means
+ // to display non-intermediate images.
+ listImagesOptions.Filters = append(listImagesOptions.Filters, "intermediate=false")
+ }
+
+ images, err := ir.Libpod.LibimageRuntime().ListImages(ctx, nil, listImagesOptions)
+ if err != nil {
+ return nil, err
}
summaries := []*entities.ImageSummary{}
@@ -30,24 +32,21 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions)
}
e := entities.ImageSummary{
- ID: img.ID(),
- ConfigDigest: string(img.ConfigDigest),
- Created: img.Created().Unix(),
- Dangling: img.Dangling(),
- Digest: string(img.Digest()),
- RepoDigests: digests,
- History: img.NamesHistory(),
- Names: img.Names(),
- ReadOnly: img.IsReadOnly(),
- SharedSize: 0,
- RepoTags: img.Names(), // may include tags and digests
+ ID: img.ID(),
+ // ConfigDigest: string(img.ConfigDigest),
+ Created: img.Created().Unix(),
+ Dangling: img.IsDangling(),
+ Digest: string(img.Digest()),
+ RepoDigests: digests,
+ History: img.NamesHistory(),
+ Names: img.Names(),
+ ReadOnly: img.IsReadOnly(),
+ SharedSize: 0,
+ RepoTags: img.Names(), // may include tags and digests
}
e.Labels, err = img.Labels(ctx)
if err != nil {
- // Ignore empty manifest lists.
- if errors.Cause(err) != libpodImage.ErrImageIsBareList {
- return nil, errors.Wrapf(err, "error retrieving label for image %q: you may need to remove the image to resolve the error", img.ID())
- }
+ return nil, errors.Wrapf(err, "error retrieving label for image %q: you may need to remove the image to resolve the error", img.ID())
}
ctnrs, err := img.Containers()
@@ -56,20 +55,22 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions)
}
e.Containers = len(ctnrs)
- sz, err := img.Size(ctx)
+ sz, err := img.Size()
if err != nil {
return nil, errors.Wrapf(err, "error retrieving size of image %q: you may need to remove the image to resolve the error", img.ID())
}
- e.Size = int64(*sz)
+ e.Size = sz
// This is good enough for now, but has to be
// replaced later with correct calculation logic
- e.VirtualSize = int64(*sz)
+ e.VirtualSize = sz
- parent, err := img.ParentID(ctx)
+ parent, err := img.Parent(ctx)
if err != nil {
return nil, errors.Wrapf(err, "error retrieving parent of image %q: you may need to remove the image to resolve the error", img.ID())
}
- e.ParentId = parent
+ if parent != nil {
+ e.ParentId = parent.ID()
+ }
summaries = append(summaries, &e)
}
diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go
index 8e3c50fac..f932cf21d 100644
--- a/pkg/domain/infra/abi/manifest.go
+++ b/pkg/domain/infra/abi/manifest.go
@@ -4,21 +4,17 @@ import (
"bytes"
"context"
"encoding/json"
- "fmt"
"os"
"strings"
- "github.com/containers/buildah/manifests"
- buildahManifests "github.com/containers/buildah/pkg/manifests"
- buildahUtil "github.com/containers/buildah/util"
+ "github.com/containers/common/libimage"
cp "github.com/containers/image/v5/copy"
- "github.com/containers/image/v5/docker"
"github.com/containers/image/v5/manifest"
+ "github.com/containers/image/v5/pkg/shortnames"
"github.com/containers/image/v5/transports"
"github.com/containers/image/v5/transports/alltransports"
- "github.com/containers/image/v5/types"
- libpodImage "github.com/containers/podman/v3/libpod/image"
"github.com/containers/podman/v3/pkg/domain/entities"
+ "github.com/containers/storage"
"github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
@@ -26,51 +22,103 @@ import (
)
// ManifestCreate implements logic for creating manifest lists via ImageEngine
-func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []string, opts entities.ManifestCreateOptions) (string, error) {
- fullNames, err := buildahUtil.ExpandNames(names, "", ir.Libpod.SystemContext(), ir.Libpod.GetStore())
- if err != nil {
- return "", errors.Wrapf(err, "error encountered while expanding image name %q", names)
+func (ir *ImageEngine) ManifestCreate(ctx context.Context, names []string, images []string, opts entities.ManifestCreateOptions) (string, error) {
+ // FIXME: change the interface of manifest create `names []string` ->
+ // `name string`.
+ if len(names) == 0 {
+ return "", errors.New("no name specified for creating a manifest list")
}
- imageID, err := libpodImage.CreateManifestList(ir.Libpod.ImageRuntime(), *ir.Libpod.SystemContext(), fullNames, images, opts.All)
+ name := names[0]
+
+ manifestList, err := ir.Libpod.LibimageRuntime().CreateManifestList(name)
if err != nil {
- return imageID, err
+ return "", err
}
- return imageID, err
+
+ addOptions := &libimage.ManifestListAddOptions{All: opts.All}
+ for _, image := range images {
+ if _, err := manifestList.Add(ctx, image, addOptions); err != nil {
+ return "", err
+ }
+ }
+
+ return manifestList.ID(), nil
}
// ManifestExists checks if a manifest list with the given name exists in local storage
func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entities.BoolReport, error) {
- if image, err := ir.Libpod.ImageRuntime().NewFromLocal(name); err == nil {
- exists, err := image.ExistsManifest()
- if err != nil && errors.Cause(err) != buildahManifests.ErrManifestTypeNotSupported {
- return nil, err
+ image, _, err := ir.Libpod.LibimageRuntime().LookupImage(name, &libimage.LookupImageOptions{IgnorePlatform: true})
+ if err != nil {
+ if errors.Cause(err) == storage.ErrImageUnknown {
+ return &entities.BoolReport{Value: false}, nil
}
- return &entities.BoolReport{Value: exists}, nil
+ return nil, err
+ }
+
+ isManifestList, err := image.IsManifestList(ctx)
+ if err != nil {
+ return nil, err
}
- return &entities.BoolReport{Value: false}, nil
+ return &entities.BoolReport{Value: isManifestList}, nil
}
// ManifestInspect returns the content of a manifest list or image
func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) {
- if newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name); err == nil {
- // return the manifest in local storage
- if list, err := newImage.InspectManifest(); err == nil {
- buf, err := json.MarshalIndent(list, "", " ")
- if err != nil {
- return buf, errors.Wrapf(err, "error rendering manifest %s for display", name)
- }
- return buf, nil
- // no return if local image is not a list of images type
- // continue on getting valid manifest through remote service
- } else if errors.Cause(err) != buildahManifests.ErrManifestTypeNotSupported {
- return nil, errors.Wrapf(err, "loading manifest %q", name)
+ // NOTE: we have to do a bit of a limbo here as `podman manifest
+ // inspect foo` wants to do a remote-inspect of foo iff "foo" in the
+ // containers storage is an ordinary image but not a manifest list.
+
+ lookupOptions := &libimage.LookupImageOptions{IgnorePlatform: true}
+ image, _, err := ir.Libpod.LibimageRuntime().LookupImage(name, lookupOptions)
+ if err != nil {
+ // If the image doesn't exist, do a remote inspect.
+ if errors.Cause(err) == storage.ErrImageUnknown {
+ return ir.remoteManifestInspect(ctx, name)
}
+ return nil, err
+ }
+
+ isManifestList, err := image.IsManifestList(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ // If the image isn't a manifest list, do a remote inspect.
+ if !isManifestList {
+ return ir.remoteManifestInspect(ctx, name)
}
- sc := ir.Libpod.SystemContext()
- refs, err := buildahUtil.ResolveNameToReferences(ir.Libpod.GetStore(), sc, name)
+
+ manifestList, err := image.ToManifestList()
if err != nil {
return nil, err
}
+
+ schema2List, err := manifestList.Inspect()
+ if err != nil {
+ return nil, err
+ }
+
+ rawSchema2List, err := json.Marshal(schema2List)
+ if err != nil {
+ return nil, err
+ }
+
+ var b bytes.Buffer
+ if err := json.Indent(&b, rawSchema2List, "", " "); err != nil {
+ return nil, errors.Wrapf(err, "error rendering manifest %s for display", name)
+ }
+ return b.Bytes(), nil
+}
+
+// inspect a remote manifest list.
+func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string) ([]byte, error) {
+ sys := ir.Libpod.SystemContext()
+
+ resolved, err := shortnames.Resolve(sys, name)
+ if err != nil {
+ return nil, err
+ }
+
var (
latestErr error
result []byte
@@ -84,8 +132,13 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte
latestErr = errors.Wrapf(latestErr, "tried %v\n", e)
}
}
- for _, ref := range refs {
- src, err := ref.NewImageSource(ctx, sc)
+
+ for _, candidate := range resolved.PullCandidates {
+ ref, err := alltransports.ParseImageName("docker://" + candidate.Value.String())
+ if err != nil {
+ return nil, err
+ }
+ src, err := ref.NewImageSource(ctx, sys)
if err != nil {
appendErr(errors.Wrapf(err, "reading image %q", transports.ImageName(ref)))
continue
@@ -102,6 +155,7 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte
manType = manifestType
break
}
+
if len(result) == 0 && latestErr != nil {
return nil, latestErr
}
@@ -138,29 +192,41 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte
// ManifestAdd adds images to the manifest list
func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAddOptions) (string, error) {
- imageSpec := opts.Images[0]
- listImageSpec := opts.Images[1]
- dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
- _, err := alltransports.ParseImageName(imageSpec)
+ // FIXME: the name options below are *mandatory* arguments and should
+ // be reflected as such in the signature.
+
+ if len(opts.Images) < 2 {
+ return "", errors.New("manifest add requires two images")
+ }
+
+ imageName := opts.Images[0]
+ listName := opts.Images[1]
+
+ manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(listName)
if err != nil {
- _, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, imageSpec))
- if err != nil {
- return "", errors.Errorf("invalid image reference %q", imageSpec)
- }
+ return "", err
}
- listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(listImageSpec)
+
+ addOptions := &libimage.ManifestListAddOptions{
+ All: opts.All,
+ AuthFilePath: opts.Authfile,
+ CertDirPath: opts.CertDir,
+ InsecureSkipTLSVerify: opts.SkipTLSVerify,
+ Username: opts.Username,
+ Password: opts.Password,
+ }
+
+ instanceDigest, err := manifestList.Add(ctx, imageName, addOptions)
if err != nil {
- return "", errors.Wrapf(err, "error retrieving local image from image name %s", listImageSpec)
+ return "", err
}
- manifestAddOpts := libpodImage.ManifestAddOpts{
- All: opts.All,
- Arch: opts.Arch,
- Features: opts.Features,
- Images: opts.Images,
- OS: opts.OS,
- OSVersion: opts.OSVersion,
- Variant: opts.Variant,
+ annotateOptions := &libimage.ManifestListAnnotateOptions{
+ Architecture: opts.Arch,
+ Features: opts.Features,
+ OS: opts.OS,
+ OSVersion: opts.OSVersion,
+ Variant: opts.Variant,
}
if len(opts.Annotation) != 0 {
annotations := make(map[string]string)
@@ -171,51 +237,44 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAd
}
annotations[spec[0]] = spec[1]
}
- manifestAddOpts.Annotation = annotations
- }
-
- // Set the system context.
- sys := ir.Libpod.SystemContext()
- if sys != nil {
- sys = &types.SystemContext{}
+ annotateOptions.Annotations = annotations
}
- sys.AuthFilePath = opts.Authfile
- sys.DockerInsecureSkipTLSVerify = opts.SkipTLSVerify
- sys.DockerCertPath = opts.CertDir
- if opts.Username != "" && opts.Password != "" {
- sys.DockerAuthConfig = &types.DockerAuthConfig{
- Username: opts.Username,
- Password: opts.Password,
- }
+ if err := manifestList.AnnotateInstance(instanceDigest, annotateOptions); err != nil {
+ return "", err
}
- listID, err := listImage.AddManifest(*sys, manifestAddOpts)
- if err != nil {
- return listID, err
- }
- return listID, nil
+ return manifestList.ID(), nil
}
// ManifestAnnotate updates an entry of the manifest list
func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opts entities.ManifestAnnotateOptions) (string, error) {
- listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0])
- if err != nil {
- return "", errors.Wrapf(err, "error retrieving local image from image name %s", names[0])
+ // FIXME: the `names` are *mandatory* arguments and should be
+ // reflected as such in the signature.
+
+ if len(names) < 2 {
+ return "", errors.New("manifest annotate requires two names")
}
- digest, err := digest.Parse(names[1])
+
+ listName := names[0]
+ instanceDigest, err := digest.Parse(names[1])
if err != nil {
return "", errors.Errorf(`invalid image digest "%s": %v`, names[1], err)
}
- manifestAnnotateOpts := libpodImage.ManifestAnnotateOpts{
- Arch: opts.Arch,
- Features: opts.Features,
- OS: opts.OS,
- OSFeatures: opts.OSFeatures,
- OSVersion: opts.OSVersion,
- Variant: opts.Variant,
+
+ manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(listName)
+ if err != nil {
+ return "", err
+ }
+
+ annotateOptions := &libimage.ManifestListAnnotateOptions{
+ Architecture: opts.Arch,
+ Features: opts.Features,
+ OS: opts.OS,
+ OSVersion: opts.OSVersion,
+ Variant: opts.Variant,
}
- if len(opts.Annotation) > 0 {
+ if len(opts.Annotation) != 0 {
annotations := make(map[string]string)
for _, annotationSpec := range opts.Annotation {
spec := strings.SplitN(annotationSpec, "=", 2)
@@ -224,48 +283,49 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opt
}
annotations[spec[0]] = spec[1]
}
- manifestAnnotateOpts.Annotation = annotations
+ annotateOptions.Annotations = annotations
}
- updatedListID, err := listImage.AnnotateManifest(*ir.Libpod.SystemContext(), digest, manifestAnnotateOpts)
- if err == nil {
- return fmt.Sprintf("%s: %s", updatedListID, digest.String()), nil
+
+ if err := manifestList.AnnotateInstance(instanceDigest, annotateOptions); err != nil {
+ return "", err
}
- return "", err
+
+ return manifestList.ID(), nil
}
// ManifestRemove removes specified digest from the specified manifest list
func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (string, error) {
+ // FIXME: the `names` are *mandatory* arguments and should be
+ // reflected as such in the signature.
+
+ if len(names) < 2 {
+ return "", errors.New("manifest remove requires two names")
+ }
+
+ listName := names[0]
instanceDigest, err := digest.Parse(names[1])
if err != nil {
return "", errors.Errorf(`invalid image digest "%s": %v`, names[1], err)
}
- listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0])
+
+ manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(listName)
if err != nil {
- return "", errors.Wrapf(err, "error retrieving local image from image name %s", names[0])
+ return "", err
}
- updatedListID, err := listImage.RemoveManifest(instanceDigest)
- if err == nil {
- return fmt.Sprintf("%s :%s\n", updatedListID, instanceDigest.String()), nil
+
+ if err := manifestList.RemoveInstance(instanceDigest); err != nil {
+ return "", err
}
- return "", err
+
+ return manifestList.ID(), nil
}
// ManifestPush pushes a manifest list or image index to the destination
func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ImagePushOptions) (string, error) {
- listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name)
+ manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(name)
if err != nil {
return "", errors.Wrapf(err, "error retrieving local image from image name %s", name)
}
- dest, err := alltransports.ParseImageName(destination)
- if err != nil {
- oldErr := err
- // Try adding the images default transport
- destination2 := libpodImage.DefaultTransport + destination
- dest, err = alltransports.ParseImageName(destination2)
- if err != nil {
- return "", oldErr
- }
- }
var manifestType string
if opts.Format != "" {
@@ -279,40 +339,33 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin
}
}
- // Set the system context.
- sys := ir.Libpod.SystemContext()
- if sys == nil {
- sys = new(types.SystemContext)
- }
- sys.AuthFilePath = opts.Authfile
- sys.DockerInsecureSkipTLSVerify = opts.SkipTLSVerify
- sys.DockerCertPath = opts.CertDir
-
- if opts.Username != "" && opts.Password != "" {
- sys.DockerAuthConfig = &types.DockerAuthConfig{
- Username: opts.Username,
- Password: opts.Password,
- }
- }
+ pushOptions := &libimage.ManifestListPushOptions{}
+ pushOptions.AuthFilePath = opts.Authfile
+ pushOptions.CertDirPath = opts.CertDir
+ pushOptions.Username = opts.Username
+ pushOptions.Password = opts.Password
+ pushOptions.ImageListSelection = cp.CopySpecificImages
+ pushOptions.ManifestMIMEType = manifestType
+ pushOptions.RemoveSignatures = opts.RemoveSignatures
+ pushOptions.SignBy = opts.SignBy
- options := manifests.PushOptions{
- Store: ir.Libpod.GetStore(),
- SystemContext: sys,
- ImageListSelection: cp.CopySpecificImages,
- Instances: nil,
- RemoveSignatures: opts.RemoveSignatures,
- SignBy: opts.SignBy,
- ManifestType: manifestType,
- }
if opts.All {
- options.ImageListSelection = cp.CopyAllImages
+ pushOptions.ImageListSelection = cp.CopyAllImages
}
if !opts.Quiet {
- options.ReportWriter = os.Stderr
+ pushOptions.Writer = os.Stderr
}
- manDigest, err := listImage.PushManifest(dest, options)
- if err == nil && opts.Rm {
- _, err = ir.Libpod.GetStore().DeleteImage(listImage.ID(), true)
+
+ manDigest, err := manifestList.Push(ctx, destination, pushOptions)
+ if err != nil {
+ return "", err
+ }
+
+ if opts.Rm {
+ if _, err := ir.Libpod.GetStore().DeleteImage(manifestList.ID(), true); err != nil {
+ return "", errors.Wrap(err, "error removing manifest after push")
+ }
}
+
return manDigest.String(), err
}
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index 6ddd4a042..d235c9ed8 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -10,17 +10,17 @@ import (
"strconv"
"strings"
+ "github.com/containers/common/libimage"
+ "github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/libpod/define"
- "github.com/containers/podman/v3/libpod/image"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/specgen/generate"
"github.com/containers/podman/v3/pkg/specgen/generate/kube"
"github.com/containers/podman/v3/pkg/util"
- "github.com/docker/distribution/reference"
"github.com/ghodss/yaml"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -154,10 +154,9 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM
func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions, ipIndex *int) (*entities.PlayKubeReport, error) {
var (
- registryCreds *types.DockerAuthConfig
- writer io.Writer
- playKubePod entities.PlayKubePod
- report entities.PlayKubeReport
+ writer io.Writer
+ playKubePod entities.PlayKubePod
+ report entities.PlayKubeReport
)
// Create the secret manager before hand
@@ -220,19 +219,6 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
writer = os.Stderr
}
- if len(options.Username) > 0 && len(options.Password) > 0 {
- registryCreds = &types.DockerAuthConfig{
- Username: options.Username,
- Password: options.Password,
- }
- }
-
- dockerRegistryOptions := image.DockerRegistryOptions{
- DockerRegistryCreds: registryCreds,
- DockerCertPath: options.CertDir,
- DockerInsecureSkipTLSVerify: options.SkipTLSVerify,
- }
-
volumes, err := kube.InitializeVolumes(podYAML.Spec.Volumes)
if err != nil {
return nil, err
@@ -273,35 +259,36 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
containers := make([]*libpod.Container, 0, len(podYAML.Spec.Containers))
for _, container := range podYAML.Spec.Containers {
- pullPolicy := util.PullImageMissing
+ // NOTE: set the pull policy to "newer". This will cover cases
+ // where the "latest" tag requires a pull and will also
+ // transparently handle "localhost/" prefixed files which *may*
+ // refer to a locally built image OR an image running a
+ // registry on localhost.
+ pullPolicy := config.PullPolicyNewer
if len(container.ImagePullPolicy) > 0 {
- pullPolicy, err = util.ValidatePullType(string(container.ImagePullPolicy))
+ pullPolicy, err = config.ParsePullPolicy(string(container.ImagePullPolicy))
if err != nil {
return nil, err
}
}
- named, err := reference.ParseNormalizedNamed(container.Image)
- if err != nil {
- return nil, errors.Wrapf(err, "Failed to parse image %q", container.Image)
- }
- // In kube, if the image is tagged with latest, it should always pull
- // but if the domain is localhost, that means the image was built locally
- // so do not attempt a pull.
- if tagged, isTagged := named.(reference.NamedTagged); isTagged {
- if tagged.Tag() == image.LatestTag && reference.Domain(named) != image.DefaultLocalRegistry {
- pullPolicy = util.PullImageAlways
- }
- }
-
// This ensures the image is the image store
- newImage, err := ic.Libpod.ImageRuntime().New(ctx, container.Image, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullPolicy, nil)
+ pullOptions := &libimage.PullOptions{}
+ pullOptions.AuthFilePath = options.Authfile
+ pullOptions.CertDirPath = options.CertDir
+ pullOptions.SignaturePolicyPath = options.SignaturePolicy
+ pullOptions.Writer = writer
+ pullOptions.Username = options.Username
+ pullOptions.Password = options.Password
+ pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify
+
+ pulledImages, err := ic.Libpod.LibimageRuntime().Pull(ctx, container.Image, pullPolicy, pullOptions)
if err != nil {
return nil, err
}
specgenOpts := kube.CtrSpecGenOptions{
Container: container,
- Image: newImage,
+ Image: pulledImages[0],
Volumes: volumes,
PodID: pod.ID(),
PodName: podName,
diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
index 9bba0fa6c..ebe59e871 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -164,7 +164,10 @@ func movePauseProcessToScope(r *libpod.Runtime) error {
// SystemPrune removes unused data from the system. Pruning pods, containers, volumes and images.
func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) {
var systemPruneReport = new(entities.SystemPruneReport)
- var filters []string
+ filters := []string{}
+ for k, v := range options.Filters {
+ filters = append(filters, fmt.Sprintf("%s=%s", k, v[0]))
+ }
reclaimedSpace := (uint64)(0)
found := true
for found {
@@ -188,10 +191,12 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys
}
reclaimedSpace = reclaimedSpace + reports.PruneReportsSize(containerPruneReports)
systemPruneReport.ContainerPruneReports = append(systemPruneReport.ContainerPruneReports, containerPruneReports...)
- for k, v := range options.Filters {
- filters = append(filters, fmt.Sprintf("%s=%s", k, v[0]))
+ imagePruneOptions := entities.ImagePruneOptions{
+ All: options.All,
+ Filter: filters,
}
- imagePruneReports, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, filters)
+ imageEngine := ImageEngine{Libpod: ic.Libpod}
+ imagePruneReports, err := imageEngine.Prune(ctx, imagePruneOptions)
reclaimedSpace = reclaimedSpace + reports.PruneReportsSize(imagePruneReports)
if err != nil {
@@ -225,13 +230,7 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System
dfImages = []*entities.SystemDfImageReport{}
)
- // Compute disk-usage stats for all local images.
- imgs, err := ic.Libpod.ImageRuntime().GetImages()
- if err != nil {
- return nil, err
- }
-
- imageStats, err := ic.Libpod.ImageRuntime().DiskUsage(ctx, imgs)
+ imageStats, err := ic.Libpod.LibimageRuntime().DiskUsage(ctx)
if err != nil {
return nil, err
}