github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/domain/infra/abi/images.go (about)

     1  package abi
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"net/url"
     9  	"os"
    10  	"path"
    11  	"path/filepath"
    12  	"strconv"
    13  	"strings"
    14  
    15  	"github.com/containers/common/pkg/config"
    16  	"github.com/containers/image/v5/docker"
    17  	"github.com/containers/image/v5/docker/reference"
    18  	"github.com/containers/image/v5/manifest"
    19  	"github.com/containers/image/v5/signature"
    20  	"github.com/containers/image/v5/transports"
    21  	"github.com/containers/image/v5/transports/alltransports"
    22  	"github.com/containers/image/v5/types"
    23  	"github.com/containers/podman/v2/libpod/define"
    24  	"github.com/containers/podman/v2/libpod/image"
    25  	libpodImage "github.com/containers/podman/v2/libpod/image"
    26  	"github.com/containers/podman/v2/pkg/domain/entities"
    27  	domainUtils "github.com/containers/podman/v2/pkg/domain/utils"
    28  	"github.com/containers/podman/v2/pkg/rootless"
    29  	"github.com/containers/podman/v2/pkg/trust"
    30  	"github.com/containers/podman/v2/pkg/util"
    31  	"github.com/containers/storage"
    32  	imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
    33  	"github.com/pkg/errors"
    34  	"github.com/sirupsen/logrus"
    35  )
    36  
    37  // SignatureStoreDir defines default directory to store signatures
    38  const SignatureStoreDir = "/var/lib/containers/sigstore"
    39  
    40  func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.BoolReport, error) {
    41  	_, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
    42  	if err != nil {
    43  		if errors.Cause(err) == define.ErrMultipleImages {
    44  			return &entities.BoolReport{Value: true}, nil
    45  		} else {
    46  			if errors.Cause(err) != define.ErrNoSuchImage {
    47  				return nil, err
    48  			}
    49  		}
    50  	}
    51  	return &entities.BoolReport{Value: err == nil}, nil
    52  }
    53  
    54  func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
    55  	results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	report := entities.ImagePruneReport{
    61  		Report: entities.Report{
    62  			Id:  results,
    63  			Err: nil,
    64  		},
    65  	}
    66  	return &report, nil
    67  }
    68  
    69  func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) {
    70  	image, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	results, err := image.History(ctx)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	history := entities.ImageHistoryReport{
    80  		Layers: make([]entities.ImageHistoryLayer, len(results)),
    81  	}
    82  
    83  	for i, layer := range results {
    84  		history.Layers[i] = ToDomainHistoryLayer(layer)
    85  	}
    86  	return &history, nil
    87  }
    88  
    89  func (ir *ImageEngine) Mount(ctx context.Context, nameOrIDs []string, opts entities.ImageMountOptions) ([]*entities.ImageMountReport, error) {
    90  	var (
    91  		images []*image.Image
    92  		err    error
    93  	)
    94  	if os.Geteuid() != 0 {
    95  		if driver := ir.Libpod.StorageConfig().GraphDriverName; driver != "vfs" {
    96  			// Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part
    97  			// of the mount command.
    98  			return nil, errors.Errorf("cannot mount using driver %s in rootless mode", driver)
    99  		}
   100  
   101  		became, ret, err := rootless.BecomeRootInUserNS("")
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  		if became {
   106  			os.Exit(ret)
   107  		}
   108  	}
   109  	if opts.All {
   110  		allImages, err := ir.Libpod.ImageRuntime().GetImages()
   111  		if err != nil {
   112  			return nil, err
   113  		}
   114  		for _, img := range allImages {
   115  			if !img.IsReadOnly() {
   116  				images = append(images, img)
   117  			}
   118  		}
   119  	} else {
   120  		for _, i := range nameOrIDs {
   121  			img, err := ir.Libpod.ImageRuntime().NewFromLocal(i)
   122  			if err != nil {
   123  				return nil, err
   124  			}
   125  			images = append(images, img)
   126  		}
   127  	}
   128  	reports := make([]*entities.ImageMountReport, 0, len(images))
   129  	for _, img := range images {
   130  		report := entities.ImageMountReport{Id: img.ID()}
   131  		if img.IsReadOnly() {
   132  			report.Err = errors.Errorf("mounting readonly %s image not supported", img.ID())
   133  		} else {
   134  			report.Path, report.Err = img.Mount([]string{}, "")
   135  		}
   136  		reports = append(reports, &report)
   137  	}
   138  	if len(reports) > 0 {
   139  		return reports, nil
   140  	}
   141  
   142  	images, err = ir.Libpod.ImageRuntime().GetImages()
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	for _, i := range images {
   147  		mounted, path, err := i.Mounted()
   148  		if err != nil {
   149  			if errors.Cause(err) == storage.ErrLayerUnknown {
   150  				continue
   151  			}
   152  			return nil, err
   153  		}
   154  		if mounted {
   155  			tags, err := i.RepoTags()
   156  			if err != nil {
   157  				return nil, err
   158  			}
   159  			reports = append(reports, &entities.ImageMountReport{
   160  				Id:           i.ID(),
   161  				Name:         string(i.Digest()),
   162  				Repositories: tags,
   163  				Path:         path,
   164  			})
   165  		}
   166  	}
   167  	return reports, nil
   168  }
   169  
   170  func (ir *ImageEngine) Unmount(ctx context.Context, nameOrIDs []string, options entities.ImageUnmountOptions) ([]*entities.ImageUnmountReport, error) {
   171  	var images []*image.Image
   172  
   173  	if options.All {
   174  		allImages, err := ir.Libpod.ImageRuntime().GetImages()
   175  		if err != nil {
   176  			return nil, err
   177  		}
   178  		for _, img := range allImages {
   179  			if !img.IsReadOnly() {
   180  				images = append(images, img)
   181  			}
   182  		}
   183  	} else {
   184  		for _, i := range nameOrIDs {
   185  			img, err := ir.Libpod.ImageRuntime().NewFromLocal(i)
   186  			if err != nil {
   187  				return nil, err
   188  			}
   189  			images = append(images, img)
   190  		}
   191  	}
   192  
   193  	reports := []*entities.ImageUnmountReport{}
   194  	for _, img := range images {
   195  		report := entities.ImageUnmountReport{Id: img.ID()}
   196  		mounted, _, err := img.Mounted()
   197  		if err != nil {
   198  			// Errors will be caught in Unmount call below
   199  			// Default assumption to mounted
   200  			mounted = true
   201  		}
   202  		if !mounted {
   203  			continue
   204  		}
   205  		if err := img.Unmount(options.Force); err != nil {
   206  			if options.All && errors.Cause(err) == storage.ErrLayerNotMounted {
   207  				logrus.Debugf("Error umounting image %s, storage.ErrLayerNotMounted", img.ID())
   208  				continue
   209  			}
   210  			report.Err = errors.Wrapf(err, "error unmounting image %s", img.ID())
   211  		}
   212  		reports = append(reports, &report)
   213  	}
   214  	return reports, nil
   215  }
   216  
   217  func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer {
   218  	l := entities.ImageHistoryLayer{}
   219  	l.ID = layer.ID
   220  	l.Created = *layer.Created
   221  	l.CreatedBy = layer.CreatedBy
   222  	copy(l.Tags, layer.Tags)
   223  	l.Size = layer.Size
   224  	l.Comment = layer.Comment
   225  	return l
   226  }
   227  
   228  func pull(ctx context.Context, runtime *image.Runtime, rawImage string, options entities.ImagePullOptions, label *string) (*entities.ImagePullReport, error) {
   229  	var writer io.Writer
   230  	if !options.Quiet {
   231  		writer = os.Stderr
   232  	}
   233  
   234  	dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
   235  	imageRef, err := alltransports.ParseImageName(rawImage)
   236  	if err != nil {
   237  		imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, rawImage))
   238  		if err != nil {
   239  			return nil, errors.Wrapf(err, "invalid image reference %q", rawImage)
   240  		}
   241  	}
   242  
   243  	var registryCreds *types.DockerAuthConfig
   244  	if len(options.Username) > 0 && len(options.Password) > 0 {
   245  		registryCreds = &types.DockerAuthConfig{
   246  			Username: options.Username,
   247  			Password: options.Password,
   248  		}
   249  	}
   250  	dockerRegistryOptions := image.DockerRegistryOptions{
   251  		DockerRegistryCreds:         registryCreds,
   252  		DockerCertPath:              options.CertDir,
   253  		OSChoice:                    options.OverrideOS,
   254  		ArchitectureChoice:          options.OverrideArch,
   255  		VariantChoice:               options.OverrideVariant,
   256  		DockerInsecureSkipTLSVerify: options.SkipTLSVerify,
   257  	}
   258  
   259  	if !options.AllTags {
   260  		newImage, err := runtime.New(ctx, rawImage, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, label, options.PullPolicy)
   261  		if err != nil {
   262  			return nil, err
   263  		}
   264  		return &entities.ImagePullReport{Images: []string{newImage.ID()}}, nil
   265  	}
   266  
   267  	// --all-tags requires the docker transport
   268  	if imageRef.Transport().Name() != docker.Transport.Name() {
   269  		return nil, errors.New("--all-tags requires docker transport")
   270  	}
   271  
   272  	// Trim the docker-transport prefix.
   273  	rawImage = strings.TrimPrefix(rawImage, docker.Transport.Name())
   274  
   275  	// all-tags doesn't work with a tagged reference, so let's check early
   276  	namedRef, err := reference.Parse(rawImage)
   277  	if err != nil {
   278  		return nil, errors.Wrapf(err, "error parsing %q", rawImage)
   279  	}
   280  	if _, isTagged := namedRef.(reference.Tagged); isTagged {
   281  		return nil, errors.New("--all-tags requires a reference without a tag")
   282  
   283  	}
   284  
   285  	systemContext := image.GetSystemContext("", options.Authfile, false)
   286  	tags, err := docker.GetRepositoryTags(ctx, systemContext, imageRef)
   287  	if err != nil {
   288  		return nil, errors.Wrapf(err, "error getting repository tags")
   289  	}
   290  
   291  	foundIDs := []string{}
   292  	for _, tag := range tags {
   293  		name := rawImage + ":" + tag
   294  		newImage, err := runtime.New(ctx, name, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways)
   295  		if err != nil {
   296  			logrus.Errorf("error pulling image %q", name)
   297  			continue
   298  		}
   299  		foundIDs = append(foundIDs, newImage.ID())
   300  	}
   301  
   302  	if len(tags) != len(foundIDs) {
   303  		return nil, err
   304  	}
   305  	return &entities.ImagePullReport{Images: foundIDs}, nil
   306  }
   307  
   308  func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) (*entities.ImagePullReport, error) {
   309  	return pull(ctx, ir.Libpod.ImageRuntime(), rawImage, options, nil)
   310  }
   311  
   312  func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts entities.InspectOptions) ([]*entities.ImageInspectReport, []error, error) {
   313  	reports := []*entities.ImageInspectReport{}
   314  	errs := []error{}
   315  	for _, i := range namesOrIDs {
   316  		img, err := ir.Libpod.ImageRuntime().NewFromLocal(i)
   317  		if err != nil {
   318  			// This is probably a no such image, treat as nonfatal.
   319  			errs = append(errs, err)
   320  			continue
   321  		}
   322  		result, err := img.Inspect(ctx)
   323  		if err != nil {
   324  			// This is more likely to be fatal.
   325  			return nil, nil, err
   326  		}
   327  		report := entities.ImageInspectReport{}
   328  		if err := domainUtils.DeepCopy(&report, result); err != nil {
   329  			return nil, nil, err
   330  		}
   331  		reports = append(reports, &report)
   332  	}
   333  	return reports, errs, nil
   334  }
   335  
   336  func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error {
   337  	var writer io.Writer
   338  	if !options.Quiet {
   339  		writer = os.Stderr
   340  	}
   341  
   342  	var manifestType string
   343  	switch options.Format {
   344  	case "":
   345  		// Default
   346  	case "oci":
   347  		manifestType = imgspecv1.MediaTypeImageManifest
   348  	case "v2s1":
   349  		manifestType = manifest.DockerV2Schema1SignedMediaType
   350  	case "v2s2", "docker":
   351  		manifestType = manifest.DockerV2Schema2MediaType
   352  	default:
   353  		return errors.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", options.Format)
   354  	}
   355  
   356  	var registryCreds *types.DockerAuthConfig
   357  	if len(options.Username) > 0 && len(options.Password) > 0 {
   358  		registryCreds = &types.DockerAuthConfig{
   359  			Username: options.Username,
   360  			Password: options.Password,
   361  		}
   362  	}
   363  	dockerRegistryOptions := image.DockerRegistryOptions{
   364  		DockerRegistryCreds:         registryCreds,
   365  		DockerCertPath:              options.CertDir,
   366  		DockerInsecureSkipTLSVerify: options.SkipTLSVerify,
   367  	}
   368  
   369  	signOptions := image.SigningOptions{
   370  		RemoveSignatures: options.RemoveSignatures,
   371  		SignBy:           options.SignBy,
   372  	}
   373  
   374  	newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(source)
   375  	if err != nil {
   376  		return err
   377  	}
   378  
   379  	return newImage.PushImageToHeuristicDestination(
   380  		ctx,
   381  		destination,
   382  		manifestType,
   383  		options.Authfile,
   384  		options.DigestFile,
   385  		options.SignaturePolicy,
   386  		writer,
   387  		options.Compress,
   388  		signOptions,
   389  		&dockerRegistryOptions,
   390  		nil)
   391  }
   392  
   393  // func (r *imageRuntime) Delete(ctx context.Context, nameOrID string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
   394  // 	image, err := r.libpod.ImageEngine().NewFromLocal(nameOrID)
   395  // 	if err != nil {
   396  // 		return nil, err
   397  // 	}
   398  //
   399  // 	results, err := r.libpod.RemoveImage(ctx, image, opts.Force)
   400  // 	if err != nil {
   401  // 		return nil, err
   402  // 	}
   403  //
   404  // 	report := entities.ImageDeleteReport{}
   405  // 	if err := domainUtils.DeepCopy(&report, results); err != nil {
   406  // 		return nil, err
   407  // 	}
   408  // 	return &report, nil
   409  // }
   410  //
   411  // func (r *imageRuntime) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
   412  // 	// TODO: map FilterOptions
   413  // 	id, err := r.libpod.ImageEngine().PruneImages(ctx, opts.All, []string{})
   414  // 	if err != nil {
   415  // 		return nil, err
   416  // 	}
   417  //
   418  // 	// TODO: Determine Size
   419  // 	report := entities.ImagePruneReport{}
   420  // 	copy(report.Report.ID, id)
   421  // 	return &report, nil
   422  // }
   423  
   424  func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, options entities.ImageTagOptions) error {
   425  	newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
   426  	if err != nil {
   427  		return err
   428  	}
   429  	for _, tag := range tags {
   430  		if err := newImage.TagImage(tag); err != nil {
   431  			return err
   432  		}
   433  	}
   434  	return nil
   435  }
   436  
   437  func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, options entities.ImageUntagOptions) error {
   438  	newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
   439  	if err != nil {
   440  		return err
   441  	}
   442  	// If only one arg is provided, all names are to be untagged
   443  	if len(tags) == 0 {
   444  		tags = newImage.Names()
   445  	}
   446  	for _, tag := range tags {
   447  		if err := newImage.UntagImage(tag); err != nil {
   448  			return err
   449  		}
   450  	}
   451  	return nil
   452  }
   453  
   454  func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) (*entities.ImageLoadReport, error) {
   455  	var (
   456  		writer io.Writer
   457  	)
   458  	if !opts.Quiet {
   459  		writer = os.Stderr
   460  	}
   461  	name, err := ir.Libpod.LoadImage(ctx, opts.Name, opts.Input, writer, opts.SignaturePolicy)
   462  	if err != nil {
   463  		return nil, err
   464  	}
   465  	names := strings.Split(name, ",")
   466  	if len(names) <= 1 {
   467  		newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name)
   468  		if err != nil {
   469  			return nil, errors.Wrap(err, "image loaded but no additional tags were created")
   470  		}
   471  		if len(opts.Name) > 0 {
   472  			if err := newImage.TagImage(fmt.Sprintf("%s:%s", opts.Name, opts.Tag)); err != nil {
   473  				return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName)
   474  			}
   475  		}
   476  	}
   477  	return &entities.ImageLoadReport{Names: names}, nil
   478  }
   479  
   480  func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) {
   481  	id, err := ir.Libpod.Import(ctx, opts.Source, opts.Reference, opts.SignaturePolicy, opts.Changes, opts.Message, opts.Quiet)
   482  	if err != nil {
   483  		return nil, err
   484  	}
   485  	return &entities.ImageImportReport{Id: id}, nil
   486  }
   487  
   488  func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error {
   489  	if options.MultiImageArchive {
   490  		nameOrIDs := append([]string{nameOrID}, tags...)
   491  		return ir.Libpod.ImageRuntime().SaveImages(ctx, nameOrIDs, options.Format, options.Output, options.Quiet, true)
   492  	}
   493  	newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
   494  	if err != nil {
   495  		return err
   496  	}
   497  	return newImage.Save(ctx, nameOrID, options.Format, options.Output, tags, options.Quiet, options.Compress, true)
   498  }
   499  
   500  func (ir *ImageEngine) Diff(_ context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) {
   501  	changes, err := ir.Libpod.GetDiff("", nameOrID)
   502  	if err != nil {
   503  		return nil, err
   504  	}
   505  	return &entities.DiffReport{Changes: changes}, nil
   506  }
   507  
   508  func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) {
   509  	filter, err := image.ParseSearchFilter(opts.Filters)
   510  	if err != nil {
   511  		return nil, err
   512  	}
   513  
   514  	searchOpts := image.SearchOptions{
   515  		Authfile:              opts.Authfile,
   516  		Filter:                *filter,
   517  		Limit:                 opts.Limit,
   518  		NoTrunc:               opts.NoTrunc,
   519  		InsecureSkipTLSVerify: opts.SkipTLSVerify,
   520  		ListTags:              opts.ListTags,
   521  	}
   522  
   523  	searchResults, err := image.SearchImages(term, searchOpts)
   524  	if err != nil {
   525  		return nil, err
   526  	}
   527  
   528  	// Convert from image.SearchResults to entities.ImageSearchReport. We don't
   529  	// want to leak any low-level packages into the remote client, which
   530  	// requires converting.
   531  	reports := make([]entities.ImageSearchReport, len(searchResults))
   532  	for i := range searchResults {
   533  		reports[i].Index = searchResults[i].Index
   534  		reports[i].Name = searchResults[i].Name
   535  		reports[i].Description = searchResults[i].Description
   536  		reports[i].Stars = searchResults[i].Stars
   537  		reports[i].Official = searchResults[i].Official
   538  		reports[i].Automated = searchResults[i].Automated
   539  		reports[i].Tag = searchResults[i].Tag
   540  	}
   541  
   542  	return reports, nil
   543  }
   544  
   545  // GetConfig returns a copy of the configuration used by the runtime
   546  func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) {
   547  	return ir.Libpod.GetConfig()
   548  }
   549  
   550  func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts entities.BuildOptions) (*entities.BuildReport, error) {
   551  
   552  	id, _, err := ir.Libpod.Build(ctx, opts.BuildOptions, containerFiles...)
   553  	if err != nil {
   554  		return nil, err
   555  	}
   556  	return &entities.BuildReport{ID: id}, nil
   557  }
   558  
   559  func (ir *ImageEngine) Tree(ctx context.Context, nameOrID string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) {
   560  	img, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
   561  	if err != nil {
   562  		return nil, err
   563  	}
   564  	results, err := img.GenerateTree(opts.WhatRequires)
   565  	if err != nil {
   566  		return nil, err
   567  	}
   568  	return &entities.ImageTreeReport{Tree: results}, nil
   569  }
   570  
   571  // removeErrorsToExitCode returns an exit code for the specified slice of
   572  // image-removal errors. The error codes are set according to the documented
   573  // behaviour in the Podman man pages.
   574  func removeErrorsToExitCode(rmErrors []error) int {
   575  	var (
   576  		// noSuchImageErrors indicates that at least one image was not found.
   577  		noSuchImageErrors bool
   578  		// inUseErrors indicates that at least one image is being used by a
   579  		// container.
   580  		inUseErrors bool
   581  		// otherErrors indicates that at least one error other than the two
   582  		// above occurred.
   583  		otherErrors bool
   584  	)
   585  
   586  	if len(rmErrors) == 0 {
   587  		return 0
   588  	}
   589  
   590  	for _, e := range rmErrors {
   591  		switch errors.Cause(e) {
   592  		case define.ErrNoSuchImage:
   593  			noSuchImageErrors = true
   594  		case define.ErrImageInUse, storage.ErrImageUsedByContainer:
   595  			inUseErrors = true
   596  		default:
   597  			otherErrors = true
   598  		}
   599  	}
   600  
   601  	switch {
   602  	case inUseErrors:
   603  		// One of the specified images has child images or is
   604  		// being used by a container.
   605  		return 2
   606  	case noSuchImageErrors && !(otherErrors || inUseErrors):
   607  		// One of the specified images did not exist, and no other
   608  		// failures.
   609  		return 1
   610  	default:
   611  		return 125
   612  	}
   613  }
   614  
   615  // Remove removes one or more images from local storage.
   616  func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entities.ImageRemoveOptions) (report *entities.ImageRemoveReport, rmErrors []error) {
   617  	report = &entities.ImageRemoveReport{}
   618  
   619  	// Set the exit code at very end.
   620  	defer func() {
   621  		report.ExitCode = removeErrorsToExitCode(rmErrors)
   622  	}()
   623  
   624  	// deleteImage is an anonymous function to conveniently delete an image
   625  	// without having to pass all local data around.
   626  	deleteImage := func(img *image.Image) error {
   627  		results, err := ir.Libpod.RemoveImage(ctx, img, opts.Force)
   628  		if err != nil {
   629  			return err
   630  		}
   631  		report.Deleted = append(report.Deleted, results.Deleted)
   632  		report.Untagged = append(report.Untagged, results.Untagged...)
   633  		return nil
   634  	}
   635  
   636  	// Delete all images from the local storage.
   637  	if opts.All {
   638  		previousImages := 0
   639  		// Remove all images one-by-one.
   640  		for {
   641  			storageImages, err := ir.Libpod.ImageRuntime().GetRWImages()
   642  			if err != nil {
   643  				rmErrors = append(rmErrors, err)
   644  				return
   645  			}
   646  			// No images (left) to remove, so we're done.
   647  			if len(storageImages) == 0 {
   648  				return
   649  			}
   650  			// Prevent infinity loops by making a delete-progress check.
   651  			if previousImages == len(storageImages) {
   652  				rmErrors = append(rmErrors, errors.New("unable to delete all images, check errors and re-run image removal if needed"))
   653  				break
   654  			}
   655  			previousImages = len(storageImages)
   656  			// Delete all "leaves" (i.e., images without child images).
   657  			for _, img := range storageImages {
   658  				isParent, err := img.IsParent(ctx)
   659  				if err != nil {
   660  					logrus.Warnf("%v, ignoring the error", err)
   661  					isParent = false
   662  				}
   663  				// Skip parent images.
   664  				if isParent {
   665  					continue
   666  				}
   667  				if err := deleteImage(img); err != nil {
   668  					rmErrors = append(rmErrors, err)
   669  				}
   670  			}
   671  		}
   672  
   673  		return
   674  	}
   675  
   676  	// Delete only the specified images.
   677  	for _, id := range images {
   678  		img, err := ir.Libpod.ImageRuntime().NewFromLocal(id)
   679  		if err != nil {
   680  			rmErrors = append(rmErrors, err)
   681  			continue
   682  		}
   683  		err = deleteImage(img)
   684  		if err != nil {
   685  			rmErrors = append(rmErrors, err)
   686  		}
   687  	}
   688  	return //nolint
   689  }
   690  
   691  // Shutdown Libpod engine
   692  func (ir *ImageEngine) Shutdown(_ context.Context) {
   693  	shutdownSync.Do(func() {
   694  		_ = ir.Libpod.Shutdown(false)
   695  	})
   696  }
   697  
   698  func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entities.SignOptions) (*entities.SignReport, error) {
   699  	mech, err := signature.NewGPGSigningMechanism()
   700  	if err != nil {
   701  		return nil, errors.Wrap(err, "error initializing GPG")
   702  	}
   703  	defer mech.Close()
   704  	if err := mech.SupportsSigning(); err != nil {
   705  		return nil, errors.Wrap(err, "signing is not supported")
   706  	}
   707  	sc := ir.Libpod.SystemContext()
   708  	sc.DockerCertPath = options.CertDir
   709  
   710  	systemRegistriesDirPath := trust.RegistriesDirPath(sc)
   711  	registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath)
   712  	if err != nil {
   713  		return nil, errors.Wrapf(err, "error reading registry configuration")
   714  	}
   715  
   716  	for _, signimage := range names {
   717  		err = func() error {
   718  			srcRef, err := alltransports.ParseImageName(signimage)
   719  			if err != nil {
   720  				return errors.Wrapf(err, "error parsing image name")
   721  			}
   722  			rawSource, err := srcRef.NewImageSource(ctx, sc)
   723  			if err != nil {
   724  				return errors.Wrapf(err, "error getting image source")
   725  			}
   726  			defer func() {
   727  				if err = rawSource.Close(); err != nil {
   728  					logrus.Errorf("unable to close %s image source %q", srcRef.DockerReference().Name(), err)
   729  				}
   730  			}()
   731  			getManifest, _, err := rawSource.GetManifest(ctx, nil)
   732  			if err != nil {
   733  				return errors.Wrapf(err, "error getting getManifest")
   734  			}
   735  			dockerReference := rawSource.Reference().DockerReference()
   736  			if dockerReference == nil {
   737  				return errors.Errorf("cannot determine canonical Docker reference for destination %s", transports.ImageName(rawSource.Reference()))
   738  			}
   739  			var sigStoreDir string
   740  			if options.Directory != "" {
   741  				sigStoreDir = options.Directory
   742  			}
   743  			if sigStoreDir == "" {
   744  				if rootless.IsRootless() {
   745  					sigStoreDir = filepath.Join(filepath.Dir(ir.Libpod.StorageConfig().GraphRoot), "sigstore")
   746  				} else {
   747  					var sigStoreURI string
   748  					registryInfo := trust.HaveMatchRegistry(rawSource.Reference().DockerReference().String(), registryConfigs)
   749  					if registryInfo != nil {
   750  						if sigStoreURI = registryInfo.SigStoreStaging; sigStoreURI == "" {
   751  							sigStoreURI = registryInfo.SigStore
   752  						}
   753  					}
   754  					if sigStoreURI == "" {
   755  						return errors.Errorf("no signature storage configuration found for %s", rawSource.Reference().DockerReference().String())
   756  
   757  					}
   758  					sigStoreDir, err = localPathFromURI(sigStoreURI)
   759  					if err != nil {
   760  						return errors.Wrapf(err, "invalid signature storage %s", sigStoreURI)
   761  					}
   762  				}
   763  			}
   764  			manifestDigest, err := manifest.Digest(getManifest)
   765  			if err != nil {
   766  				return err
   767  			}
   768  			repo := reference.Path(dockerReference)
   769  			if path.Clean(repo) != repo { // Coverage: This should not be reachable because /./ and /../ components are not valid in docker references
   770  				return errors.Errorf("Unexpected path elements in Docker reference %s for signature storage", dockerReference.String())
   771  			}
   772  
   773  			// create signature
   774  			newSig, err := signature.SignDockerManifest(getManifest, dockerReference.String(), mech, options.SignBy)
   775  			if err != nil {
   776  				return errors.Wrapf(err, "error creating new signature")
   777  			}
   778  			// create the signstore file
   779  			signatureDir := fmt.Sprintf("%s@%s=%s", filepath.Join(sigStoreDir, repo), manifestDigest.Algorithm(), manifestDigest.Hex())
   780  			if err := os.MkdirAll(signatureDir, 0751); err != nil {
   781  				// The directory is allowed to exist
   782  				if !os.IsExist(err) {
   783  					logrus.Error(err)
   784  					return nil
   785  				}
   786  			}
   787  			sigFilename, err := getSigFilename(signatureDir)
   788  			if err != nil {
   789  				logrus.Errorf("error creating sigstore file: %v", err)
   790  				return nil
   791  			}
   792  			err = ioutil.WriteFile(filepath.Join(signatureDir, sigFilename), newSig, 0644)
   793  			if err != nil {
   794  				logrus.Errorf("error storing signature for %s", rawSource.Reference().DockerReference().String())
   795  				return nil
   796  			}
   797  			return nil
   798  		}()
   799  		if err != nil {
   800  			return nil, err
   801  		}
   802  	}
   803  	return nil, nil
   804  }
   805  
   806  func getSigFilename(sigStoreDirPath string) (string, error) {
   807  	sigFileSuffix := 1
   808  	sigFiles, err := ioutil.ReadDir(sigStoreDirPath)
   809  	if err != nil {
   810  		return "", err
   811  	}
   812  	sigFilenames := make(map[string]bool)
   813  	for _, file := range sigFiles {
   814  		sigFilenames[file.Name()] = true
   815  	}
   816  	for {
   817  		sigFilename := "signature-" + strconv.Itoa(sigFileSuffix)
   818  		if _, exists := sigFilenames[sigFilename]; !exists {
   819  			return sigFilename, nil
   820  		}
   821  		sigFileSuffix++
   822  	}
   823  }
   824  
   825  func localPathFromURI(sigStoreDir string) (string, error) {
   826  	url, err := url.Parse(sigStoreDir)
   827  	if err != nil {
   828  		return sigStoreDir, errors.Wrapf(err, "invalid directory %s", sigStoreDir)
   829  	}
   830  	if url.Scheme != "file" {
   831  		return sigStoreDir, errors.Errorf("writing to %s is not supported. Use a supported scheme", sigStoreDir)
   832  	}
   833  	sigStoreDir = url.Path
   834  	return sigStoreDir, nil
   835  }