github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/domain/infra/tunnel/images.go (about)

     1  package tunnel
     2  
     3  import (
     4  	"context"
     5  	"io/ioutil"
     6  	"os"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/containers/common/libimage"
    12  	"github.com/containers/common/pkg/config"
    13  	"github.com/containers/image/v5/docker/reference"
    14  	"github.com/containers/image/v5/types"
    15  	"github.com/hanks177/podman/v4/libpod/define"
    16  	"github.com/hanks177/podman/v4/pkg/bindings/images"
    17  	"github.com/hanks177/podman/v4/pkg/domain/entities"
    18  	"github.com/hanks177/podman/v4/pkg/domain/entities/reports"
    19  	"github.com/hanks177/podman/v4/pkg/domain/utils"
    20  	"github.com/hanks177/podman/v4/pkg/errorhandling"
    21  	utils2 "github.com/hanks177/podman/v4/utils"
    22  	"github.com/pkg/errors"
    23  )
    24  
    25  func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.BoolReport, error) {
    26  	found, err := images.Exists(ir.ClientCtx, nameOrID, nil)
    27  	return &entities.BoolReport{Value: found}, err
    28  }
    29  
    30  func (ir *ImageEngine) Remove(ctx context.Context, imagesArg []string, opts entities.ImageRemoveOptions) (*entities.ImageRemoveReport, []error) {
    31  	options := new(images.RemoveOptions).WithForce(opts.Force).WithIgnore(opts.Ignore).WithAll(opts.All)
    32  	return images.Remove(ir.ClientCtx, imagesArg, options)
    33  }
    34  
    35  func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) {
    36  	filters := make(map[string][]string, len(opts.Filter))
    37  	for _, filter := range opts.Filter {
    38  		f := strings.Split(filter, "=")
    39  		filters[f[0]] = f[1:]
    40  	}
    41  	options := new(images.ListOptions).WithAll(opts.All).WithFilters(filters)
    42  	psImages, err := images.List(ir.ClientCtx, options)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	is := make([]*entities.ImageSummary, len(psImages))
    48  	for i, img := range psImages {
    49  		hold := entities.ImageSummary{}
    50  		if err := utils.DeepCopy(&hold, img); err != nil {
    51  			return nil, err
    52  		}
    53  		is[i] = &hold
    54  	}
    55  	return is, nil
    56  }
    57  
    58  func (ir *ImageEngine) Mount(ctx context.Context, images []string, options entities.ImageMountOptions) ([]*entities.ImageMountReport, error) {
    59  	return nil, errors.New("mounting images is not supported for remote clients")
    60  }
    61  
    62  func (ir *ImageEngine) Unmount(ctx context.Context, images []string, options entities.ImageUnmountOptions) ([]*entities.ImageUnmountReport, error) {
    63  	return nil, errors.New("unmounting images is not supported for remote clients")
    64  }
    65  
    66  func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) {
    67  	options := new(images.HistoryOptions)
    68  	results, err := images.History(ir.ClientCtx, nameOrID, options)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	history := entities.ImageHistoryReport{
    74  		Layers: make([]entities.ImageHistoryLayer, len(results)),
    75  	}
    76  
    77  	for i, layer := range results {
    78  		// Created time comes over as an int64 so needs conversion to time.time
    79  		t := time.Unix(layer.Created, 0)
    80  		hold := entities.ImageHistoryLayer{
    81  			ID:        layer.ID,
    82  			Created:   t.UTC(),
    83  			CreatedBy: layer.CreatedBy,
    84  			Tags:      layer.Tags,
    85  			Size:      layer.Size,
    86  			Comment:   layer.Comment,
    87  		}
    88  		history.Layers[i] = hold
    89  	}
    90  	return &history, nil
    91  }
    92  
    93  func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) ([]*reports.PruneReport, error) {
    94  	filters := make(map[string][]string, len(opts.Filter))
    95  	for _, filter := range opts.Filter {
    96  		f := strings.Split(filter, "=")
    97  		filters[f[0]] = f[1:]
    98  	}
    99  	options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters).WithExternal(opts.External)
   100  	reports, err := images.Prune(ir.ClientCtx, options)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	return reports, nil
   105  }
   106  
   107  func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.ImagePullOptions) (*entities.ImagePullReport, error) {
   108  	options := new(images.PullOptions)
   109  	options.WithAllTags(opts.AllTags).WithAuthfile(opts.Authfile).WithArch(opts.Arch).WithOS(opts.OS)
   110  	options.WithVariant(opts.Variant).WithPassword(opts.Password)
   111  	options.WithQuiet(opts.Quiet).WithUsername(opts.Username).WithPolicy(opts.PullPolicy.String())
   112  	if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
   113  		if s == types.OptionalBoolTrue {
   114  			options.WithSkipTLSVerify(true)
   115  		} else {
   116  			options.WithSkipTLSVerify(false)
   117  		}
   118  	}
   119  	pulledImages, err := images.Pull(ir.ClientCtx, rawImage, options)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	return &entities.ImagePullReport{Images: pulledImages}, nil
   124  }
   125  
   126  type pullResult struct {
   127  	images []*libimage.Image
   128  	err    error
   129  }
   130  
   131  func (ir *ImageEngine) PullImage(ctx context.Context, rawImage string, pullOptions *libimage.PullOptions)  error {
   132  	return errors.New("not support yet")
   133  }
   134  
   135  func (ir *ImageEngine) Transfer(ctx context.Context, source entities.ImageScpOptions, dest entities.ImageScpOptions, parentFlags []string) error {
   136  	return errors.Wrapf(define.ErrNotImplemented, "cannot use the remote client to transfer images between root and rootless storage")
   137  }
   138  
   139  func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, opt entities.ImageTagOptions) error {
   140  	options := new(images.TagOptions)
   141  	for _, newTag := range tags {
   142  		var (
   143  			tag, repo string
   144  		)
   145  		ref, err := reference.Parse(newTag)
   146  		if err != nil {
   147  			return errors.Wrapf(err, "error parsing reference %q", newTag)
   148  		}
   149  		if t, ok := ref.(reference.Tagged); ok {
   150  			tag = t.Tag()
   151  		}
   152  		if r, ok := ref.(reference.Named); ok {
   153  			repo = r.Name()
   154  		}
   155  		if len(repo) < 1 {
   156  			return errors.Errorf("invalid image name %q", nameOrID)
   157  		}
   158  		if err := images.Tag(ir.ClientCtx, nameOrID, tag, repo, options); err != nil {
   159  			return err
   160  		}
   161  	}
   162  	return nil
   163  }
   164  
   165  func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, opt entities.ImageUntagOptions) error {
   166  	options := new(images.UntagOptions)
   167  	if len(tags) == 0 {
   168  		return images.Untag(ir.ClientCtx, nameOrID, "", "", options)
   169  	}
   170  
   171  	for _, newTag := range tags {
   172  		var (
   173  			tag, repo string
   174  		)
   175  		ref, err := reference.Parse(newTag)
   176  		if err != nil {
   177  			return errors.Wrapf(err, "error parsing reference %q", newTag)
   178  		}
   179  		if t, ok := ref.(reference.Tagged); ok {
   180  			tag = t.Tag()
   181  		}
   182  		if t, ok := ref.(reference.Digested); ok {
   183  			tag += "@" + t.Digest().String()
   184  		}
   185  		if r, ok := ref.(reference.Named); ok {
   186  			repo = r.Name()
   187  		}
   188  		if len(repo) < 1 {
   189  			return errors.Errorf("invalid image name %q", nameOrID)
   190  		}
   191  		if err := images.Untag(ir.ClientCtx, nameOrID, tag, repo, options); err != nil {
   192  			return err
   193  		}
   194  	}
   195  	return nil
   196  }
   197  
   198  func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts entities.InspectOptions) ([]*entities.ImageInspectReport, []error, error) {
   199  	options := new(images.GetOptions).WithSize(opts.Size)
   200  	reports := []*entities.ImageInspectReport{}
   201  	errs := []error{}
   202  	for _, i := range namesOrIDs {
   203  		r, err := images.GetImage(ir.ClientCtx, i, options)
   204  		if err != nil {
   205  			errModel, ok := err.(*errorhandling.ErrorModel)
   206  			if !ok {
   207  				return nil, nil, err
   208  			}
   209  			if errModel.ResponseCode == 404 {
   210  				errs = append(errs, errors.Wrapf(err, "unable to inspect %q", i))
   211  				continue
   212  			}
   213  			return nil, nil, err
   214  		}
   215  		reports = append(reports, r)
   216  	}
   217  	return reports, errs, nil
   218  }
   219  
   220  func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) (*entities.ImageLoadReport, error) {
   221  	f, err := os.Open(opts.Input)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  	defer f.Close()
   226  	fInfo, err := f.Stat()
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  	if fInfo.IsDir() {
   231  		return nil, errors.Errorf("remote client supports archives only but %q is a directory", opts.Input)
   232  	}
   233  	return images.Load(ir.ClientCtx, f)
   234  }
   235  
   236  func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) {
   237  	var (
   238  		err error
   239  		f   *os.File
   240  	)
   241  	options := new(images.ImportOptions).WithChanges(opts.Changes).WithMessage(opts.Message).WithReference(opts.Reference)
   242  	options.WithOS(opts.OS).WithArchitecture(opts.Architecture).WithVariant(opts.Variant)
   243  	if opts.SourceIsURL {
   244  		options.WithURL(opts.Source)
   245  	} else {
   246  		f, err = os.Open(opts.Source)
   247  		if err != nil {
   248  			return nil, err
   249  		}
   250  	}
   251  	return images.Import(ir.ClientCtx, f, options)
   252  }
   253  
   254  func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, opts entities.ImagePushOptions) error {
   255  	options := new(images.PushOptions)
   256  	options.WithAll(opts.All).WithCompress(opts.Compress).WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile).WithFormat(opts.Format)
   257  
   258  	if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
   259  		if s == types.OptionalBoolTrue {
   260  			options.WithSkipTLSVerify(true)
   261  		} else {
   262  			options.WithSkipTLSVerify(false)
   263  		}
   264  	}
   265  	return images.Push(ir.ClientCtx, source, destination, options)
   266  }
   267  
   268  func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, opts entities.ImageSaveOptions) error {
   269  	var (
   270  		f   *os.File
   271  		err error
   272  	)
   273  	options := new(images.ExportOptions).WithFormat(opts.Format).WithCompress(opts.Compress)
   274  	options = options.WithOciAcceptUncompressedLayers(opts.OciAcceptUncompressedLayers)
   275  
   276  	switch opts.Format {
   277  	case "oci-dir", "docker-dir":
   278  		f, err = ioutil.TempFile("", "podman_save")
   279  		if err == nil {
   280  			defer func() { _ = os.Remove(f.Name()) }()
   281  		}
   282  	default:
   283  		// This code was added to allow for opening stdout replacing
   284  		// os.Create(opts.Output) which was attempting to open the file
   285  		// for read/write which fails on Darwin platforms
   286  		f, err = os.OpenFile(opts.Output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
   287  	}
   288  	if err != nil {
   289  		return err
   290  	}
   291  
   292  	exErr := images.Export(ir.ClientCtx, append([]string{nameOrID}, tags...), f, options)
   293  	if err := f.Close(); err != nil {
   294  		return err
   295  	}
   296  	if exErr != nil {
   297  		return exErr
   298  	}
   299  
   300  	if opts.Format != "oci-dir" && opts.Format != "docker-dir" {
   301  		return nil
   302  	}
   303  
   304  	f, err = os.Open(f.Name())
   305  	if err != nil {
   306  		return err
   307  	}
   308  	info, err := os.Stat(opts.Output)
   309  	switch {
   310  	case err == nil:
   311  		if info.Mode().IsRegular() {
   312  			return errors.Errorf("%q already exists as a regular file", opts.Output)
   313  		}
   314  	case os.IsNotExist(err):
   315  		if err := os.Mkdir(opts.Output, 0755); err != nil {
   316  			return err
   317  		}
   318  	default:
   319  		return err
   320  	}
   321  	return utils2.UntarToFileSystem(opts.Output, f, nil)
   322  }
   323  
   324  func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) {
   325  	mappedFilters := make(map[string][]string)
   326  	filters, err := libimage.ParseSearchFilter(opts.Filters)
   327  	if err != nil {
   328  		return nil, err
   329  	}
   330  	if stars := filters.Stars; stars > 0 {
   331  		mappedFilters["stars"] = []string{strconv.Itoa(stars)}
   332  	}
   333  
   334  	if official := filters.IsOfficial; official != types.OptionalBoolUndefined {
   335  		mappedFilters["is-official"] = []string{strconv.FormatBool(official == types.OptionalBoolTrue)}
   336  	}
   337  
   338  	if automated := filters.IsAutomated; automated != types.OptionalBoolUndefined {
   339  		mappedFilters["is-automated"] = []string{strconv.FormatBool(automated == types.OptionalBoolTrue)}
   340  	}
   341  
   342  	options := new(images.SearchOptions)
   343  	options.WithAuthfile(opts.Authfile).WithFilters(mappedFilters).WithLimit(opts.Limit)
   344  	options.WithListTags(opts.ListTags)
   345  	if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
   346  		if s == types.OptionalBoolTrue {
   347  			options.WithSkipTLSVerify(true)
   348  		} else {
   349  			options.WithSkipTLSVerify(false)
   350  		}
   351  	}
   352  	return images.Search(ir.ClientCtx, term, options)
   353  }
   354  
   355  func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) {
   356  	return config.Default()
   357  }
   358  
   359  func (ir *ImageEngine) Build(_ context.Context, containerFiles []string, opts entities.BuildOptions) (*entities.BuildReport, error) {
   360  	report, err := images.Build(ir.ClientCtx, containerFiles, opts)
   361  	if err != nil {
   362  		return nil, err
   363  	}
   364  	return report, nil
   365  }
   366  
   367  func (ir *ImageEngine) Tree(ctx context.Context, nameOrID string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) {
   368  	options := new(images.TreeOptions).WithWhatRequires(opts.WhatRequires)
   369  	return images.Tree(ir.ClientCtx, nameOrID, options)
   370  }
   371  
   372  // Shutdown Libpod engine
   373  func (ir *ImageEngine) Shutdown(_ context.Context) {
   374  }
   375  
   376  func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entities.SignOptions) (*entities.SignReport, error) {
   377  	return nil, errors.New("not implemented yet")
   378  }