diff options
Diffstat (limited to 'pkg/domain/infra/abi/images.go')
-rw-r--r-- | pkg/domain/infra/abi/images.go | 343 |
1 files changed, 328 insertions, 15 deletions
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 6e9d7f566..9d706a112 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -4,38 +4,116 @@ package abi import ( "context" + "fmt" + "io" + "os" + "strings" + "github.com/containers/image/v5/docker" + dockerarchive "github.com/containers/image/v5/docker/archive" + "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/manifest" + "github.com/containers/image/v5/transports/alltransports" + "github.com/containers/image/v5/types" + "github.com/containers/libpod/libpod/image" libpodImage "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/domain/entities" - "github.com/containers/libpod/pkg/domain/utils" + domainUtils "github.com/containers/libpod/pkg/domain/utils" + "github.com/containers/libpod/pkg/util" + "github.com/containers/storage" + imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) -func (ir *ImageEngine) Delete(ctx context.Context, nameOrId string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) { - image, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) - if err != nil { - return nil, err +func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.BoolReport, error) { + if _, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId); err != nil { + return &entities.BoolReport{}, nil } + return &entities.BoolReport{Value: true}, nil +} - results, err := ir.Libpod.RemoveImage(ctx, image, opts.Force) - if err != nil { - return nil, err +func (ir *ImageEngine) Delete(ctx context.Context, nameOrId []string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) { + report := entities.ImageDeleteReport{} + + if opts.All { + var previousTargets []*libpodImage.Image + repeatRun: + targets, err := ir.Libpod.ImageRuntime().GetRWImages() + if err != nil { + return &report, errors.Wrapf(err, "unable to query local images") + } + + if len(targets) > 0 && len(targets) == len(previousTargets) { + return &report, errors.New("unable to delete all images; re-run the rmi command again.") + } + previousTargets = targets + + for _, img := range targets { + isParent, err := img.IsParent(ctx) + if err != nil { + return &report, err + } + if isParent { + continue + } + err = ir.deleteImage(ctx, img, opts, report) + report.Errors = append(report.Errors, err) + } + if len(previousTargets) != 1 { + goto repeatRun + } + return &report, nil } - report := entities.ImageDeleteReport{} - if err := utils.DeepCopy(&report, results); err != nil { - return nil, err + for _, id := range nameOrId { + image, err := ir.Libpod.ImageRuntime().NewFromLocal(id) + if err != nil { + return nil, err + } + + err = ir.deleteImage(ctx, image, opts, report) + if err != nil { + return &report, err + } } return &report, nil } +func (ir *ImageEngine) deleteImage(ctx context.Context, img *libpodImage.Image, opts entities.ImageDeleteOptions, report entities.ImageDeleteReport) error { + results, err := ir.Libpod.RemoveImage(ctx, img, opts.Force) + switch errors.Cause(err) { + case nil: + break + case storage.ErrImageUsedByContainer: + report.ImageInUse = errors.New( + fmt.Sprintf("A container associated with containers/storage, i.e. via Buildah, CRI-O, etc., may be associated with this image: %-12.12s\n", img.ID())) + return nil + case libpodImage.ErrNoSuchImage: + report.ImageNotFound = err + return nil + default: + return err + } + + report.Deleted = append(report.Deleted, results.Deleted) + report.Untagged = append(report.Untagged, results.Untagged...) + return nil +} + func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) { - results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, []string{}) + results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter) if err != nil { return nil, err } - report := entities.ImagePruneReport{} - copy(report.Report.Id, results) + report := entities.ImagePruneReport{ + Report: entities.Report{ + Id: results, + Err: nil, + }, + Size: 0, + } return &report, nil } @@ -70,6 +148,178 @@ func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer return l } +func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) (*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.Errorf("invalid image reference %q", rawImage) + } + } + + // Special-case for docker-archive which allows multiple tags. + if imageRef.Transport().Name() == dockerarchive.Transport.Name() { + newImage, err := ir.Libpod.ImageRuntime().LoadFromArchiveReference(ctx, imageRef, options.SignaturePolicy, writer) + if err != nil { + return nil, errors.Wrapf(err, "error pulling image %q", rawImage) + } + return &entities.ImagePullReport{Images: []string{newImage[0].ID()}}, nil + } + + var registryCreds *types.DockerAuthConfig + if options.Credentials != "" { + creds, err := util.ParseRegistryCreds(options.Credentials) + if err != nil { + return nil, err + } + registryCreds = creds + } + dockerRegistryOptions := image.DockerRegistryOptions{ + DockerRegistryCreds: registryCreds, + DockerCertPath: options.CertDir, + OSChoice: options.OverrideOS, + ArchitectureChoice: options.OverrideArch, + DockerInsecureSkipTLSVerify: options.TLSVerify, + } + + if !options.AllTags { + newImage, err := ir.Libpod.ImageRuntime().New(ctx, rawImage, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways) + if err != nil { + return nil, errors.Wrapf(err, "error pulling image %q", rawImage) + } + 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") + } + + // Trim the docker-transport prefix. + rawImage = strings.TrimPrefix(rawImage, docker.Transport.Name()) + + // 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") + + } + + systemContext := image.GetSystemContext("", options.Authfile, false) + tags, err := docker.GetRepositoryTags(ctx, systemContext, imageRef) + if err != nil { + return nil, errors.Wrapf(err, "error getting repository tags") + } + + var foundIDs []string + for _, tag := range tags { + name := rawImage + ":" + tag + newImage, err := ir.Libpod.ImageRuntime().New(ctx, name, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways) + if err != nil { + logrus.Errorf("error pulling image %q", name) + continue + } + foundIDs = append(foundIDs, newImage.ID()) + } + + if len(tags) != len(foundIDs) { + return nil, errors.Errorf("error pulling image %q", rawImage) + } + return &entities.ImagePullReport{Images: foundIDs}, nil +} + +func (ir *ImageEngine) Inspect(ctx context.Context, names []string, opts entities.InspectOptions) (*entities.ImageInspectReport, error) { + report := entities.ImageInspectReport{ + Errors: make(map[string]error), + } + + for _, id := range names { + img, err := ir.Libpod.ImageRuntime().NewFromLocal(id) + if err != nil { + report.Errors[id] = err + continue + } + + results, err := img.Inspect(ctx) + if err != nil { + report.Errors[id] = err + continue + } + + cookedResults := entities.ImageData{} + _ = domainUtils.DeepCopy(&cookedResults, results) + report.Images = append(report.Images, &cookedResults) + } + return &report, nil +} + +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 "": + // Default + case "oci": + manifestType = imgspecv1.MediaTypeImageManifest + case "v2s1": + manifestType = manifest.DockerV2Schema1SignedMediaType + case "v2s2", "docker": + manifestType = manifest.DockerV2Schema2MediaType + default: + return fmt.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", options.Format) + } + + var registryCreds *types.DockerAuthConfig + if options.Credentials != "" { + creds, err := util.ParseRegistryCreds(options.Credentials) + if err != nil { + return err + } + registryCreds = creds + } + dockerRegistryOptions := image.DockerRegistryOptions{ + DockerRegistryCreds: registryCreds, + DockerCertPath: options.CertDir, + DockerInsecureSkipTLSVerify: options.TLSVerify, + } + + signOptions := image.SigningOptions{ + RemoveSignatures: options.RemoveSignatures, + SignBy: options.SignBy, + } + + newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(source) + if err != nil { + return err + } + + return newImage.PushImageToHeuristicDestination( + ctx, + destination, + manifestType, + options.Authfile, + options.DigestFile, + options.SignaturePolicy, + writer, + options.Compress, + signOptions, + &dockerRegistryOptions, + nil) +} + // func (r *imageRuntime) Delete(ctx context.Context, nameOrId string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) { // image, err := r.libpod.ImageEngine().NewFromLocal(nameOrId) // if err != nil { @@ -82,7 +332,7 @@ func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer // } // // report := entities.ImageDeleteReport{} -// if err := utils.DeepCopy(&report, results); err != nil { +// if err := domainUtils.DeepCopy(&report, results); err != nil { // return nil, err // } // return &report, nil @@ -100,3 +350,66 @@ func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer // copy(report.Report.Id, id) // return &report, nil // } + +func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string, options entities.ImageTagOptions) error { + newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) + if err != nil { + return err + } + for _, tag := range tags { + if err := newImage.TagImage(tag); err != nil { + return err + } + } + return nil +} + +func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string, options entities.ImageUntagOptions) error { + newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) + if err != nil { + return err + } + for _, tag := range tags { + if err := newImage.UntagImage(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.Name, opts.Input, writer, opts.SignaturePolicy) + if err != nil { + return nil, err + } + newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name) + if err != nil { + return nil, errors.Wrap(err, "image loaded but no additional tags were created") + } + if err := newImage.TagImage(opts.Name); err != nil { + return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName) + } + return &entities.ImageLoadReport{Name: 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.Changes, opts.Message, opts.Quiet) + if err != nil { + return nil, err + } + return &entities.ImageImportReport{Id: id}, nil +} + +func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, options entities.ImageSaveOptions) error { + newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) + if err != nil { + return err + } + return newImage.Save(ctx, nameOrId, options.Format, options.Output, tags, options.Quiet, options.Compress) +} |