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

     1  package tunnel
     2  
     3  import (
     4  	"context"
     5  	"io/ioutil"
     6  	"os"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/containers/common/pkg/config"
    11  	"github.com/containers/image/v5/docker/reference"
    12  	images "github.com/containers/podman/v2/pkg/bindings/images"
    13  	"github.com/containers/podman/v2/pkg/domain/entities"
    14  	"github.com/containers/podman/v2/pkg/domain/utils"
    15  	utils2 "github.com/containers/podman/v2/utils"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.BoolReport, error) {
    20  	found, err := images.Exists(ir.ClientCxt, nameOrID)
    21  	return &entities.BoolReport{Value: found}, err
    22  }
    23  
    24  func (ir *ImageEngine) Remove(ctx context.Context, imagesArg []string, opts entities.ImageRemoveOptions) (*entities.ImageRemoveReport, []error) {
    25  	return images.BatchRemove(ir.ClientCxt, imagesArg, opts)
    26  }
    27  
    28  func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) {
    29  
    30  	filters := make(map[string][]string, len(opts.Filter))
    31  	for _, filter := range opts.Filter {
    32  		f := strings.Split(filter, "=")
    33  		filters[f[0]] = f[1:]
    34  	}
    35  	images, err := images.List(ir.ClientCxt, &opts.All, filters)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  
    40  	is := make([]*entities.ImageSummary, len(images))
    41  	for i, img := range images {
    42  		hold := entities.ImageSummary{}
    43  		if err := utils.DeepCopy(&hold, img); err != nil {
    44  			return nil, err
    45  		}
    46  		is[i] = &hold
    47  	}
    48  	return is, nil
    49  }
    50  
    51  func (ir *ImageEngine) Mount(ctx context.Context, images []string, options entities.ImageMountOptions) ([]*entities.ImageMountReport, error) {
    52  	return nil, errors.New("mounting images is not supported for remote clients")
    53  }
    54  
    55  func (ir *ImageEngine) Unmount(ctx context.Context, images []string, options entities.ImageUnmountOptions) ([]*entities.ImageUnmountReport, error) {
    56  	return nil, errors.New("unmounting images is not supported for remote clients")
    57  }
    58  
    59  func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) {
    60  	results, err := images.History(ir.ClientCxt, nameOrID)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	history := entities.ImageHistoryReport{
    66  		Layers: make([]entities.ImageHistoryLayer, len(results)),
    67  	}
    68  
    69  	for i, layer := range results {
    70  		// Created time comes over as an int64 so needs conversion to time.time
    71  		t := time.Unix(layer.Created, 0)
    72  		hold := entities.ImageHistoryLayer{
    73  			ID:        layer.ID,
    74  			Created:   t.UTC(),
    75  			CreatedBy: layer.CreatedBy,
    76  			Tags:      layer.Tags,
    77  			Size:      layer.Size,
    78  			Comment:   layer.Comment,
    79  		}
    80  		history.Layers[i] = hold
    81  	}
    82  	return &history, nil
    83  }
    84  
    85  func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
    86  	filters := make(map[string][]string, len(opts.Filter))
    87  	for _, filter := range opts.Filter {
    88  		f := strings.Split(filter, "=")
    89  		filters[f[0]] = f[1:]
    90  	}
    91  
    92  	results, err := images.Prune(ir.ClientCxt, &opts.All, filters)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	report := entities.ImagePruneReport{
    98  		Report: entities.Report{
    99  			Id:  results,
   100  			Err: nil,
   101  		},
   102  		Size: 0,
   103  	}
   104  	return &report, nil
   105  }
   106  
   107  func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) (*entities.ImagePullReport, error) {
   108  	pulledImages, err := images.Pull(ir.ClientCxt, rawImage, options)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	return &entities.ImagePullReport{Images: pulledImages}, nil
   113  }
   114  
   115  func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, options entities.ImageTagOptions) error {
   116  	for _, newTag := range tags {
   117  		var (
   118  			tag, repo string
   119  		)
   120  		ref, err := reference.Parse(newTag)
   121  		if err != nil {
   122  			return errors.Wrapf(err, "error parsing reference %q", newTag)
   123  		}
   124  		if t, ok := ref.(reference.Tagged); ok {
   125  			tag = t.Tag()
   126  		}
   127  		if r, ok := ref.(reference.Named); ok {
   128  			repo = r.Name()
   129  		}
   130  		if len(repo) < 1 {
   131  			return errors.Errorf("invalid image name %q", nameOrID)
   132  		}
   133  		if err := images.Tag(ir.ClientCxt, nameOrID, tag, repo); err != nil {
   134  			return err
   135  		}
   136  	}
   137  	return nil
   138  }
   139  
   140  func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, options entities.ImageUntagOptions) error {
   141  	if len(tags) == 0 {
   142  		return images.Untag(ir.ClientCxt, nameOrID, "", "")
   143  	}
   144  
   145  	for _, newTag := range tags {
   146  		var (
   147  			tag, repo string
   148  		)
   149  		ref, err := reference.Parse(newTag)
   150  		if err != nil {
   151  			return errors.Wrapf(err, "error parsing reference %q", newTag)
   152  		}
   153  		if t, ok := ref.(reference.Tagged); ok {
   154  			tag = t.Tag()
   155  		}
   156  		if r, ok := ref.(reference.Named); ok {
   157  			repo = r.Name()
   158  		}
   159  		if len(repo) < 1 {
   160  			return errors.Errorf("invalid image name %q", nameOrID)
   161  		}
   162  		if err := images.Untag(ir.ClientCxt, nameOrID, tag, repo); err != nil {
   163  			return err
   164  		}
   165  	}
   166  	return nil
   167  }
   168  
   169  func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts entities.InspectOptions) ([]*entities.ImageInspectReport, []error, error) {
   170  	reports := []*entities.ImageInspectReport{}
   171  	errs := []error{}
   172  	for _, i := range namesOrIDs {
   173  		r, err := images.GetImage(ir.ClientCxt, i, &opts.Size)
   174  		if err != nil {
   175  			errModel, ok := err.(entities.ErrorModel)
   176  			if !ok {
   177  				return nil, nil, err
   178  			}
   179  			if errModel.ResponseCode == 404 {
   180  				errs = append(errs, errors.Wrapf(err, "unable to inspect %q", i))
   181  				continue
   182  			}
   183  			return nil, nil, err
   184  		}
   185  		reports = append(reports, r)
   186  	}
   187  	return reports, errs, nil
   188  }
   189  
   190  func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) (*entities.ImageLoadReport, error) {
   191  	f, err := os.Open(opts.Input)
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  	defer f.Close()
   196  	fInfo, err := f.Stat()
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  	if fInfo.IsDir() {
   201  		return nil, errors.Errorf("remote client supports archives only but %q is a directory", opts.Input)
   202  	}
   203  	ref := opts.Name
   204  	if len(opts.Tag) > 0 {
   205  		ref += ":" + opts.Tag
   206  	}
   207  	return images.Load(ir.ClientCxt, f, &ref)
   208  }
   209  
   210  func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) {
   211  	var (
   212  		err       error
   213  		sourceURL *string
   214  		f         *os.File
   215  	)
   216  	if opts.SourceIsURL {
   217  		sourceURL = &opts.Source
   218  	} else {
   219  		f, err = os.Open(opts.Source)
   220  		if err != nil {
   221  			return nil, err
   222  		}
   223  	}
   224  	return images.Import(ir.ClientCxt, opts.Changes, &opts.Message, &opts.Reference, sourceURL, f)
   225  }
   226  
   227  func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error {
   228  	return images.Push(ir.ClientCxt, source, destination, options)
   229  }
   230  
   231  func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error {
   232  	var (
   233  		f   *os.File
   234  		err error
   235  	)
   236  	switch options.Format {
   237  	case "oci-dir", "docker-dir":
   238  		f, err = ioutil.TempFile("", "podman_save")
   239  		if err == nil {
   240  			defer func() { _ = os.Remove(f.Name()) }()
   241  		}
   242  	default:
   243  		f, err = os.Create(options.Output)
   244  	}
   245  	if err != nil {
   246  		return err
   247  	}
   248  
   249  	if options.MultiImageArchive {
   250  		exErr := images.MultiExport(ir.ClientCxt, append([]string{nameOrID}, tags...), f, &options.Format, &options.Compress)
   251  		if err := f.Close(); err != nil {
   252  			return err
   253  		}
   254  		if exErr != nil {
   255  			return exErr
   256  		}
   257  	} else {
   258  		// FIXME: tags are entirely ignored here but shouldn't.
   259  		exErr := images.Export(ir.ClientCxt, nameOrID, f, &options.Format, &options.Compress)
   260  		if err := f.Close(); err != nil {
   261  			return err
   262  		}
   263  		if exErr != nil {
   264  			return exErr
   265  		}
   266  	}
   267  
   268  	if options.Format != "oci-dir" && options.Format != "docker-dir" {
   269  		return nil
   270  	}
   271  
   272  	f, err = os.Open(f.Name())
   273  	if err != nil {
   274  		return err
   275  	}
   276  	info, err := os.Stat(options.Output)
   277  	switch {
   278  	case err == nil:
   279  		if info.Mode().IsRegular() {
   280  			return errors.Errorf("%q already exists as a regular file", options.Output)
   281  		}
   282  	case os.IsNotExist(err):
   283  		if err := os.Mkdir(options.Output, 0755); err != nil {
   284  			return err
   285  		}
   286  	default:
   287  		return err
   288  	}
   289  	return utils2.UntarToFileSystem(options.Output, f, nil)
   290  }
   291  
   292  // Diff reports the changes to the given image
   293  func (ir *ImageEngine) Diff(ctx context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) {
   294  	changes, err := images.Diff(ir.ClientCxt, nameOrID)
   295  	if err != nil {
   296  		return nil, err
   297  	}
   298  	return &entities.DiffReport{Changes: changes}, nil
   299  }
   300  
   301  func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) {
   302  	return images.Search(ir.ClientCxt, term, opts)
   303  }
   304  
   305  func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) {
   306  	return config.Default()
   307  }
   308  
   309  func (ir *ImageEngine) Build(_ context.Context, containerFiles []string, opts entities.BuildOptions) (*entities.BuildReport, error) {
   310  	report, err := images.Build(ir.ClientCxt, containerFiles, opts)
   311  	if err != nil {
   312  		return nil, err
   313  	}
   314  	// For remote clients, if the option for writing to a file was
   315  	// selected, we need to write to the *client's* filesystem.
   316  	if len(opts.IIDFile) > 0 {
   317  		f, err := os.Create(opts.IIDFile)
   318  		if err != nil {
   319  			return nil, err
   320  		}
   321  		if _, err := f.WriteString(report.ID); err != nil {
   322  			return nil, err
   323  		}
   324  	}
   325  	return report, nil
   326  }
   327  
   328  func (ir *ImageEngine) Tree(ctx context.Context, nameOrID string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) {
   329  	return images.Tree(ir.ClientCxt, nameOrID, &opts.WhatRequires)
   330  }
   331  
   332  // Shutdown Libpod engine
   333  func (ir *ImageEngine) Shutdown(_ context.Context) {
   334  }
   335  
   336  func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entities.SignOptions) (*entities.SignReport, error) {
   337  	return nil, errors.New("not implemented yet")
   338  }