github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/adapter/runtime_remote.go (about)

     1  // +build remoteclient
     2  
     3  package adapter
     4  
     5  import (
     6  	"bufio"
     7  	"context"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  	"text/template"
    16  	"time"
    17  
    18  	"github.com/containers/buildah/imagebuildah"
    19  	"github.com/containers/buildah/pkg/formats"
    20  	"github.com/containers/common/pkg/config"
    21  	"github.com/containers/image/v5/docker/reference"
    22  	"github.com/containers/image/v5/types"
    23  	"github.com/containers/libpod/cmd/podman/cliconfig"
    24  	"github.com/containers/libpod/cmd/podman/remoteclientconfig"
    25  	"github.com/containers/libpod/libpod"
    26  	"github.com/containers/libpod/libpod/define"
    27  	"github.com/containers/libpod/libpod/events"
    28  	"github.com/containers/libpod/libpod/image"
    29  	"github.com/containers/libpod/pkg/util"
    30  	iopodman "github.com/containers/libpod/pkg/varlink"
    31  	"github.com/containers/libpod/utils"
    32  	"github.com/containers/storage/pkg/archive"
    33  	"github.com/opencontainers/go-digest"
    34  	"github.com/pkg/errors"
    35  	"github.com/sirupsen/logrus"
    36  	"github.com/varlink/go/varlink"
    37  	v1 "k8s.io/api/core/v1"
    38  )
    39  
    40  // ImageRuntime is wrapper for image runtime
    41  type RemoteImageRuntime struct{}
    42  
    43  // RemoteRuntime describes a wrapper runtime struct
    44  type RemoteRuntime struct {
    45  	Conn   *varlink.Connection
    46  	Remote bool
    47  	cmd    cliconfig.MainFlags
    48  	config io.Reader
    49  }
    50  
    51  // LocalRuntime describes a typical libpod runtime
    52  type LocalRuntime struct {
    53  	*RemoteRuntime
    54  }
    55  
    56  // GetRuntimeNoStore returns a LocalRuntime struct with the actual runtime embedded in it
    57  // The nostore is ignored
    58  func GetRuntimeNoStore(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) {
    59  	return GetRuntime(ctx, c)
    60  }
    61  
    62  // GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it
    63  func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) {
    64  	var (
    65  		customConfig bool
    66  		err          error
    67  		f            *os.File
    68  	)
    69  	runtime := RemoteRuntime{
    70  		Remote: true,
    71  		cmd:    c.GlobalFlags,
    72  	}
    73  	configPath := remoteclientconfig.GetConfigFilePath()
    74  	// Check if the basedir for configPath exists and if not, create it.
    75  	if _, err := os.Stat(filepath.Dir(configPath)); os.IsNotExist(err) {
    76  		if mkdirErr := os.MkdirAll(filepath.Dir(configPath), 0750); mkdirErr != nil {
    77  			return nil, mkdirErr
    78  		}
    79  	}
    80  	if len(c.GlobalFlags.RemoteConfigFilePath) > 0 {
    81  		configPath = c.GlobalFlags.RemoteConfigFilePath
    82  		customConfig = true
    83  	}
    84  
    85  	f, err = os.Open(configPath)
    86  	if err != nil {
    87  		// If user does not explicitly provide a configuration file path and we cannot
    88  		// find a default, no error should occur.
    89  		if os.IsNotExist(err) && !customConfig {
    90  			logrus.Debugf("unable to load configuration file at %s", configPath)
    91  			runtime.config = nil
    92  		} else {
    93  			return nil, errors.Wrapf(err, "unable to load configuration file at %s", configPath)
    94  		}
    95  	} else {
    96  		// create the io reader for the remote client
    97  		runtime.config = bufio.NewReader(f)
    98  	}
    99  	conn, err := runtime.Connect()
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	runtime.Conn = conn
   104  	return &LocalRuntime{
   105  		&runtime,
   106  	}, nil
   107  }
   108  
   109  // DeferredShutdown is a bogus wrapper for compaat with the libpod
   110  // runtime and should only be run when a defer is being used
   111  func (r RemoteRuntime) DeferredShutdown(force bool) {
   112  	if err := r.Shutdown(force); err != nil {
   113  		logrus.Error("unable to shutdown runtime")
   114  	}
   115  }
   116  
   117  // Containers is a bogus wrapper for compat with the libpod runtime
   118  type ContainersConfig struct {
   119  	// CGroupManager is the CGroup Manager to use
   120  	// Valid values are "cgroupfs" and "systemd"
   121  	CgroupManager string
   122  }
   123  
   124  // RuntimeConfig is a bogus wrapper for compat with the libpod runtime
   125  type RuntimeConfig struct {
   126  	Containers ContainersConfig
   127  }
   128  
   129  // Shutdown is a bogus wrapper for compat with the libpod runtime
   130  func (r *RemoteRuntime) GetConfig() (*config.Config, error) {
   131  	return nil, nil
   132  }
   133  
   134  // Shutdown is a bogus wrapper for compat with the libpod runtime
   135  func (r RemoteRuntime) Shutdown(force bool) error {
   136  	return nil
   137  }
   138  
   139  // ContainerImage
   140  type ContainerImage struct {
   141  	remoteImage
   142  }
   143  
   144  type remoteImage struct {
   145  	ID           string
   146  	Labels       map[string]string
   147  	RepoTags     []string
   148  	RepoDigests  []string
   149  	Parent       string
   150  	Size         int64
   151  	Created      time.Time
   152  	InputName    string
   153  	Names        []string
   154  	Digest       digest.Digest
   155  	Digests      []digest.Digest
   156  	isParent     bool
   157  	Runtime      *LocalRuntime
   158  	TopLayer     string
   159  	ReadOnly     bool
   160  	NamesHistory []string
   161  }
   162  
   163  // Container ...
   164  type Container struct {
   165  	remoteContainer
   166  }
   167  
   168  // remoteContainer ....
   169  type remoteContainer struct {
   170  	Runtime *LocalRuntime
   171  	config  *libpod.ContainerConfig
   172  	state   *libpod.ContainerState
   173  }
   174  
   175  // Pod ...
   176  type Pod struct {
   177  	remotepod
   178  }
   179  
   180  type remotepod struct {
   181  	config     *libpod.PodConfig
   182  	state      *libpod.PodInspectState
   183  	containers []libpod.PodContainerInfo // nolint: structcheck
   184  	Runtime    *LocalRuntime
   185  }
   186  
   187  type VolumeFilter func(*Volume) bool
   188  
   189  // Volume is embed for libpod volumes
   190  type Volume struct {
   191  	remoteVolume
   192  }
   193  
   194  type remoteVolume struct {
   195  	Runtime *LocalRuntime
   196  	config  *libpod.VolumeConfig
   197  }
   198  
   199  // GetImages returns a slice of containerimages over a varlink connection
   200  func (r *LocalRuntime) GetImages() ([]*ContainerImage, error) {
   201  	return r.getImages(false)
   202  }
   203  
   204  // GetRWImages returns a slice of read/write containerimages over a varlink connection
   205  func (r *LocalRuntime) GetRWImages() ([]*ContainerImage, error) {
   206  	return r.getImages(true)
   207  }
   208  
   209  func (r *LocalRuntime) GetFilteredImages(filters []string, rwOnly bool) ([]*ContainerImage, error) {
   210  	if len(filters) > 0 {
   211  		return nil, errors.Wrap(define.ErrNotImplemented, "filtering images is not supported on the remote client")
   212  	}
   213  	var newImages []*ContainerImage
   214  	images, err := iopodman.ListImages().Call(r.Conn)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  	for _, i := range images {
   219  		if rwOnly && i.ReadOnly {
   220  			continue
   221  		}
   222  		name := i.Id
   223  		if len(i.RepoTags) > 1 {
   224  			name = i.RepoTags[0]
   225  		}
   226  		newImage, err := imageInListToContainerImage(i, name, r)
   227  		if err != nil {
   228  			return nil, err
   229  		}
   230  		newImages = append(newImages, newImage)
   231  	}
   232  	return newImages, nil
   233  }
   234  func (r *LocalRuntime) getImages(rwOnly bool) ([]*ContainerImage, error) {
   235  	var newImages []*ContainerImage
   236  	images, err := iopodman.ListImages().Call(r.Conn)
   237  	if err != nil {
   238  		return nil, err
   239  	}
   240  	for _, i := range images {
   241  		if rwOnly && i.ReadOnly {
   242  			continue
   243  		}
   244  		name := i.Id
   245  		if len(i.RepoTags) > 1 {
   246  			name = i.RepoTags[0]
   247  		}
   248  		newImage, err := imageInListToContainerImage(i, name, r)
   249  		if err != nil {
   250  			return nil, err
   251  		}
   252  		newImages = append(newImages, newImage)
   253  	}
   254  	return newImages, nil
   255  }
   256  
   257  func imageInListToContainerImage(i iopodman.Image, name string, runtime *LocalRuntime) (*ContainerImage, error) {
   258  	created, err := time.ParseInLocation(time.RFC3339, i.Created, time.UTC)
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  	var digests []digest.Digest
   263  	for _, d := range i.Digests {
   264  		digests = append(digests, digest.Digest(d))
   265  	}
   266  	ri := remoteImage{
   267  		InputName:    name,
   268  		ID:           i.Id,
   269  		Digest:       digest.Digest(i.Digest),
   270  		Digests:      digests,
   271  		Labels:       i.Labels,
   272  		RepoTags:     i.RepoTags,
   273  		RepoDigests:  i.RepoTags,
   274  		Parent:       i.ParentId,
   275  		Size:         i.Size,
   276  		Created:      created,
   277  		Names:        i.RepoTags,
   278  		isParent:     i.IsParent,
   279  		Runtime:      runtime,
   280  		TopLayer:     i.TopLayer,
   281  		ReadOnly:     i.ReadOnly,
   282  		NamesHistory: i.History,
   283  	}
   284  	return &ContainerImage{ri}, nil
   285  }
   286  
   287  // NewImageFromLocal returns a container image representation of a image over varlink
   288  func (r *LocalRuntime) NewImageFromLocal(name string) (*ContainerImage, error) {
   289  	img, err := iopodman.GetImage().Call(r.Conn, name)
   290  	if err != nil {
   291  		return nil, err
   292  	}
   293  	return imageInListToContainerImage(img, name, r)
   294  
   295  }
   296  
   297  // LoadFromArchiveReference creates an image from a local archive
   298  func (r *LocalRuntime) LoadFromArchiveReference(ctx context.Context, srcRef types.ImageReference, signaturePolicyPath string, writer io.Writer) ([]*ContainerImage, error) {
   299  	var iid string
   300  	creds := iopodman.AuthConfig{}
   301  	reply, err := iopodman.PullImage().Send(r.Conn, varlink.More, srcRef.DockerReference().String(), creds)
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  
   306  	for {
   307  		responses, flags, err := reply()
   308  		if err != nil {
   309  			return nil, err
   310  		}
   311  		for _, line := range responses.Logs {
   312  			fmt.Print(line)
   313  		}
   314  		iid = responses.Id
   315  		if flags&varlink.Continues == 0 {
   316  			break
   317  		}
   318  	}
   319  
   320  	newImage, err := r.NewImageFromLocal(iid)
   321  	if err != nil {
   322  		return nil, err
   323  	}
   324  	return []*ContainerImage{newImage}, nil
   325  }
   326  
   327  // New calls into local storage to look for an image in local storage or to pull it
   328  func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *image.DockerRegistryOptions, signingoptions image.SigningOptions, label *string, pullType util.PullType) (*ContainerImage, error) {
   329  	var iid string
   330  	if label != nil {
   331  		return nil, errors.New("the remote client function does not support checking a remote image for a label")
   332  	}
   333  	creds := iopodman.AuthConfig{}
   334  	if dockeroptions.DockerRegistryCreds != nil {
   335  		creds.Username = dockeroptions.DockerRegistryCreds.Username
   336  		creds.Password = dockeroptions.DockerRegistryCreds.Password
   337  	}
   338  	reply, err := iopodman.PullImage().Send(r.Conn, varlink.More, name, creds)
   339  	if err != nil {
   340  		return nil, err
   341  	}
   342  	for {
   343  		responses, flags, err := reply()
   344  		if err != nil {
   345  			return nil, err
   346  		}
   347  		for _, line := range responses.Logs {
   348  			fmt.Print(line)
   349  		}
   350  		iid = responses.Id
   351  		if flags&varlink.Continues == 0 {
   352  			break
   353  		}
   354  	}
   355  	newImage, err := r.NewImageFromLocal(iid)
   356  	if err != nil {
   357  		return nil, err
   358  	}
   359  	return newImage, nil
   360  }
   361  
   362  func (r *LocalRuntime) ImageTree(imageOrID string, whatRequires bool) (string, error) {
   363  	return iopodman.ImageTree().Call(r.Conn, imageOrID, whatRequires)
   364  }
   365  
   366  // IsParent goes through the layers in the store and checks if i.TopLayer is
   367  // the parent of any other layer in store. Double check that image with that
   368  // layer exists as well.
   369  func (ci *ContainerImage) IsParent(context.Context) (bool, error) {
   370  	return ci.remoteImage.isParent, nil
   371  }
   372  
   373  // ID returns the image ID as a string
   374  func (ci *ContainerImage) ID() string {
   375  	return ci.remoteImage.ID
   376  }
   377  
   378  // Names returns a string array of names associated with the image
   379  func (ci *ContainerImage) Names() []string {
   380  	return ci.remoteImage.Names
   381  }
   382  
   383  // NamesHistory returns a string array of names previously associated with the image
   384  func (ci *ContainerImage) NamesHistory() []string {
   385  	return ci.remoteImage.NamesHistory
   386  }
   387  
   388  // Created returns the time the image was created
   389  func (ci *ContainerImage) Created() time.Time {
   390  	return ci.remoteImage.Created
   391  }
   392  
   393  // IsReadOnly returns whether the image is ReadOnly
   394  func (ci *ContainerImage) IsReadOnly() bool {
   395  	return ci.remoteImage.ReadOnly
   396  }
   397  
   398  // Size returns the size of the image
   399  func (ci *ContainerImage) Size(ctx context.Context) (*uint64, error) {
   400  	usize := uint64(ci.remoteImage.Size)
   401  	return &usize, nil
   402  }
   403  
   404  // Digest returns the image's digest
   405  func (ci *ContainerImage) Digest() digest.Digest {
   406  	return ci.remoteImage.Digest
   407  }
   408  
   409  // Digests returns the image's digests
   410  func (ci *ContainerImage) Digests() []digest.Digest {
   411  	return append([]digest.Digest{}, ci.remoteImage.Digests...)
   412  }
   413  
   414  // Labels returns a map of the image's labels
   415  func (ci *ContainerImage) Labels(ctx context.Context) (map[string]string, error) {
   416  	return ci.remoteImage.Labels, nil
   417  }
   418  
   419  // Dangling returns a bool if the image is "dangling"
   420  func (ci *ContainerImage) Dangling() bool {
   421  	return len(ci.Names()) == 0
   422  }
   423  
   424  // TopLayer returns an images top layer as a string
   425  func (ci *ContainerImage) TopLayer() string {
   426  	return ci.remoteImage.TopLayer
   427  }
   428  
   429  // TagImage ...
   430  func (ci *ContainerImage) TagImage(tag string) error {
   431  	_, err := iopodman.TagImage().Call(ci.Runtime.Conn, ci.ID(), tag)
   432  	return err
   433  }
   434  
   435  // UntagImage removes a single tag from an image
   436  func (ci *ContainerImage) UntagImage(tag string) error {
   437  	_, err := iopodman.UntagImage().Call(ci.Runtime.Conn, ci.ID(), tag)
   438  	return err
   439  }
   440  
   441  // RemoveImage calls varlink to remove an image
   442  func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, force bool) (*image.ImageDeleteResponse, error) {
   443  	ir := image.ImageDeleteResponse{}
   444  	response, err := iopodman.RemoveImageWithResponse().Call(r.Conn, img.InputName, force)
   445  	if err != nil {
   446  		return nil, err
   447  	}
   448  	ir.Deleted = response.Deleted
   449  	ir.Untagged = append(ir.Untagged, response.Untagged...)
   450  	return &ir, nil
   451  }
   452  
   453  // History returns the history of an image and its layers
   454  func (ci *ContainerImage) History(ctx context.Context) ([]*image.History, error) {
   455  	var imageHistories []*image.History
   456  
   457  	reply, err := iopodman.HistoryImage().Call(ci.Runtime.Conn, ci.InputName)
   458  	if err != nil {
   459  		return nil, err
   460  	}
   461  	for _, h := range reply {
   462  		created, err := time.ParseInLocation(time.RFC3339, h.Created, time.UTC)
   463  		if err != nil {
   464  			return nil, err
   465  		}
   466  		ih := image.History{
   467  			ID:        h.Id,
   468  			Created:   &created,
   469  			CreatedBy: h.CreatedBy,
   470  			Size:      h.Size,
   471  			Comment:   h.Comment,
   472  		}
   473  		imageHistories = append(imageHistories, &ih)
   474  	}
   475  	return imageHistories, nil
   476  }
   477  
   478  // PruneImages is the wrapper call for a remote-client to prune images
   479  func (r *LocalRuntime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) {
   480  	return iopodman.ImagesPrune().Call(r.Conn, all, filter)
   481  }
   482  
   483  // Export is a wrapper to container export to a tarfile
   484  func (r *LocalRuntime) Export(name string, path string) error {
   485  	tempPath, err := iopodman.ExportContainer().Call(r.Conn, name, "")
   486  	if err != nil {
   487  		return err
   488  	}
   489  	return r.GetFileFromRemoteHost(tempPath, path, true)
   490  }
   491  
   492  func (r *LocalRuntime) GetFileFromRemoteHost(remoteFilePath, outputPath string, delete bool) error {
   493  	outputFile, err := os.Create(outputPath)
   494  	if err != nil {
   495  		return err
   496  	}
   497  	defer outputFile.Close()
   498  
   499  	writer := bufio.NewWriter(outputFile)
   500  	defer writer.Flush()
   501  
   502  	reply, err := iopodman.ReceiveFile().Send(r.Conn, varlink.Upgrade, remoteFilePath, delete)
   503  	if err != nil {
   504  		return err
   505  	}
   506  
   507  	length, _, err := reply()
   508  	if err != nil {
   509  		return errors.Wrap(err, "unable to get file length for transfer")
   510  	}
   511  
   512  	reader := r.Conn.Reader
   513  	if _, err := io.CopyN(writer, reader, length); err != nil {
   514  		return errors.Wrap(err, "file transfer failed")
   515  	}
   516  	return nil
   517  }
   518  
   519  // Import implements the remote calls required to import a container image to the store
   520  func (r *LocalRuntime) Import(ctx context.Context, source, reference string, changes []string, history string, quiet bool) (string, error) {
   521  	// First we send the file to the host
   522  	tempFile, err := r.SendFileOverVarlink(source)
   523  	if err != nil {
   524  		return "", err
   525  	}
   526  	return iopodman.ImportImage().Call(r.Conn, strings.TrimRight(tempFile, ":"), reference, history, changes, true)
   527  }
   528  
   529  func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, options imagebuildah.BuildOptions, dockerfiles []string) (string, reference.Canonical, error) {
   530  	buildOptions := iopodman.BuildOptions{
   531  		AddHosts:     options.CommonBuildOpts.AddHost,
   532  		CgroupParent: options.CommonBuildOpts.CgroupParent,
   533  		CpuPeriod:    int64(options.CommonBuildOpts.CPUPeriod),
   534  		CpuQuota:     options.CommonBuildOpts.CPUQuota,
   535  		CpuShares:    int64(options.CommonBuildOpts.CPUShares),
   536  		CpusetCpus:   options.CommonBuildOpts.CPUSetMems,
   537  		CpusetMems:   options.CommonBuildOpts.CPUSetMems,
   538  		Memory:       options.CommonBuildOpts.Memory,
   539  		MemorySwap:   options.CommonBuildOpts.MemorySwap,
   540  		ShmSize:      options.CommonBuildOpts.ShmSize,
   541  		Ulimit:       options.CommonBuildOpts.Ulimit,
   542  		Volume:       options.CommonBuildOpts.Volumes,
   543  	}
   544  	buildinfo := iopodman.BuildInfo{
   545  		// Err: string(options.Err),
   546  		// Out:
   547  		// ReportWriter:
   548  		Architecture:            options.Architecture,
   549  		AddCapabilities:         options.AddCapabilities,
   550  		AdditionalTags:          options.AdditionalTags,
   551  		Annotations:             options.Annotations,
   552  		BuildArgs:               options.Args,
   553  		BuildOptions:            buildOptions,
   554  		CniConfigDir:            options.CNIConfigDir,
   555  		CniPluginDir:            options.CNIPluginPath,
   556  		Compression:             string(options.Compression),
   557  		Devices:                 options.Devices,
   558  		DefaultsMountFilePath:   options.DefaultMountsFilePath,
   559  		Dockerfiles:             dockerfiles,
   560  		DropCapabilities:        options.DropCapabilities,
   561  		ForceRmIntermediateCtrs: options.ForceRmIntermediateCtrs,
   562  		Iidfile:                 options.IIDFile,
   563  		Label:                   options.Labels,
   564  		Layers:                  options.Layers,
   565  		//		NamespaceOptions:        options.NamespaceOptions,
   566  		Nocache:                options.NoCache,
   567  		Os:                     options.OS,
   568  		Output:                 options.Output,
   569  		OutputFormat:           options.OutputFormat,
   570  		PullPolicy:             options.PullPolicy.String(),
   571  		Quiet:                  options.Quiet,
   572  		RemoteIntermediateCtrs: options.RemoveIntermediateCtrs,
   573  		RuntimeArgs:            options.RuntimeArgs,
   574  		SignBy:                 options.SignBy,
   575  		Squash:                 options.Squash,
   576  		Target:                 options.Target,
   577  		TransientMounts:        options.TransientMounts,
   578  	}
   579  	// tar the file
   580  	outputFile, err := ioutil.TempFile("", "varlink_tar_send")
   581  	if err != nil {
   582  		return "", nil, err
   583  	}
   584  	defer outputFile.Close()
   585  	defer os.Remove(outputFile.Name())
   586  
   587  	// Create the tarball of the context dir to a tempfile
   588  	if err := utils.TarToFilesystem(options.ContextDirectory, outputFile); err != nil {
   589  		return "", nil, err
   590  	}
   591  	// Send the context dir tarball over varlink.
   592  	tempFile, err := r.SendFileOverVarlink(outputFile.Name())
   593  	if err != nil {
   594  		return "", nil, err
   595  	}
   596  	buildinfo.ContextDir = tempFile
   597  
   598  	reply, err := iopodman.BuildImage().Send(r.Conn, varlink.More, buildinfo)
   599  	if err != nil {
   600  		return "", nil, err
   601  	}
   602  
   603  	for {
   604  		responses, flags, err := reply()
   605  		if err != nil {
   606  			return "", nil, err
   607  		}
   608  		for _, line := range responses.Logs {
   609  			fmt.Print(line)
   610  		}
   611  		if flags&varlink.Continues == 0 {
   612  			break
   613  		}
   614  	}
   615  	return "", nil, err
   616  }
   617  
   618  // SendFileOverVarlink sends a file over varlink in an upgraded connection
   619  func (r *LocalRuntime) SendFileOverVarlink(source string) (string, error) {
   620  	fs, err := os.Open(source)
   621  	if err != nil {
   622  		return "", err
   623  	}
   624  
   625  	fileInfo, err := fs.Stat()
   626  	if err != nil {
   627  		return "", err
   628  	}
   629  	logrus.Debugf("sending %s over varlink connection", source)
   630  	reply, err := iopodman.SendFile().Send(r.Conn, varlink.Upgrade, "", fileInfo.Size())
   631  	if err != nil {
   632  		return "", err
   633  	}
   634  	_, _, err = reply()
   635  	if err != nil {
   636  		return "", err
   637  	}
   638  
   639  	reader := bufio.NewReader(fs)
   640  	_, err = reader.WriteTo(r.Conn.Writer)
   641  	if err != nil {
   642  		return "", err
   643  	}
   644  	logrus.Debugf("file transfer complete for %s", source)
   645  	r.Conn.Writer.Flush()
   646  
   647  	// All was sent, wait for the ACK from the server
   648  	tempFile, err := r.Conn.Reader.ReadString(':')
   649  	if err != nil {
   650  		return "", err
   651  	}
   652  
   653  	// r.Conn is kaput at this point due to the upgrade
   654  	if err := r.RemoteRuntime.RefreshConnection(); err != nil {
   655  		return "", err
   656  
   657  	}
   658  
   659  	return strings.Replace(tempFile, ":", "", -1), nil
   660  }
   661  
   662  // GetAllVolumes retrieves all the volumes
   663  func (r *LocalRuntime) GetAllVolumes() ([]*libpod.Volume, error) {
   664  	return nil, define.ErrNotImplemented
   665  }
   666  
   667  // RemoveVolume removes a volumes
   668  func (r *LocalRuntime) RemoveVolume(ctx context.Context, v *libpod.Volume, force, prune bool) error {
   669  	return define.ErrNotImplemented
   670  }
   671  
   672  // GetContainers retrieves all containers from the state
   673  // Filters can be provided which will determine what containers are included in
   674  // the output. Multiple filters are handled by ANDing their output, so only
   675  // containers matching all filters are returned
   676  func (r *LocalRuntime) GetContainers(filters ...libpod.ContainerFilter) ([]*libpod.Container, error) {
   677  	return nil, define.ErrNotImplemented
   678  }
   679  
   680  // RemoveContainer removes the given container
   681  // If force is specified, the container will be stopped first
   682  // Otherwise, RemoveContainer will return an error if the container is running
   683  func (r *LocalRuntime) RemoveContainer(ctx context.Context, c *libpod.Container, force, volumes bool) error {
   684  	return define.ErrNotImplemented
   685  }
   686  
   687  // CreateVolume creates a volume over a varlink connection for the remote client
   688  func (r *LocalRuntime) CreateVolume(ctx context.Context, c *cliconfig.VolumeCreateValues, labels, opts map[string]string) (string, error) {
   689  	cvOpts := iopodman.VolumeCreateOpts{
   690  		Options: opts,
   691  		Labels:  labels,
   692  	}
   693  	if len(c.InputArgs) > 0 {
   694  		cvOpts.VolumeName = c.InputArgs[0]
   695  	}
   696  
   697  	if c.Flag("driver").Changed {
   698  		cvOpts.Driver = c.Driver
   699  	}
   700  
   701  	return iopodman.VolumeCreate().Call(r.Conn, cvOpts)
   702  }
   703  
   704  // RemoveVolumes removes volumes over a varlink connection for the remote client
   705  func (r *LocalRuntime) RemoveVolumes(ctx context.Context, c *cliconfig.VolumeRmValues) ([]string, map[string]error, error) {
   706  	rmOpts := iopodman.VolumeRemoveOpts{
   707  		All:     c.All,
   708  		Force:   c.Force,
   709  		Volumes: c.InputArgs,
   710  	}
   711  	success, failures, err := iopodman.VolumeRemove().Call(r.Conn, rmOpts)
   712  	stringsToErrors := make(map[string]error)
   713  	for k, v := range failures {
   714  		stringsToErrors[k] = errors.New(v)
   715  	}
   716  	return success, stringsToErrors, err
   717  }
   718  
   719  func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, digestfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error {
   720  
   721  	reply, err := iopodman.PushImage().Send(r.Conn, varlink.More, srcName, destination, forceCompress, manifestMIMEType, signingOptions.RemoveSignatures, signingOptions.SignBy)
   722  	if err != nil {
   723  		return err
   724  	}
   725  	for {
   726  		responses, flags, err := reply()
   727  		if err != nil {
   728  			return err
   729  		}
   730  		for _, line := range responses.Logs {
   731  			fmt.Print(line)
   732  		}
   733  		if flags&varlink.Continues == 0 {
   734  			break
   735  		}
   736  	}
   737  
   738  	return err
   739  }
   740  
   741  // InspectVolumes returns a slice of volumes based on an arg list or --all
   742  func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*libpod.InspectVolumeData, error) {
   743  	var (
   744  		inspectData []*libpod.InspectVolumeData
   745  		volumes     []string
   746  	)
   747  
   748  	if c.All {
   749  		allVolumes, err := r.Volumes(ctx)
   750  		if err != nil {
   751  			return nil, err
   752  		}
   753  		for _, vol := range allVolumes {
   754  			volumes = append(volumes, vol.Name())
   755  		}
   756  	} else {
   757  		volumes = append(volumes, c.InputArgs...)
   758  	}
   759  
   760  	for _, vol := range volumes {
   761  		jsonString, err := iopodman.InspectVolume().Call(r.Conn, vol)
   762  		if err != nil {
   763  			return nil, err
   764  		}
   765  		inspectJSON := new(libpod.InspectVolumeData)
   766  		if err := json.Unmarshal([]byte(jsonString), inspectJSON); err != nil {
   767  			return nil, errors.Wrapf(err, "error unmarshalling inspect JSON for volume %s", vol)
   768  		}
   769  		inspectData = append(inspectData, inspectJSON)
   770  	}
   771  
   772  	return inspectData, nil
   773  }
   774  
   775  // Volumes returns a slice of adapter.volumes based on information about libpod
   776  // volumes over a varlink connection
   777  func (r *LocalRuntime) Volumes(ctx context.Context) ([]*Volume, error) {
   778  	reply, err := iopodman.GetVolumes().Call(r.Conn, []string{}, true)
   779  	if err != nil {
   780  		return nil, err
   781  	}
   782  	return varlinkVolumeToVolume(r, reply), nil
   783  }
   784  
   785  func varlinkVolumeToVolume(r *LocalRuntime, volumes []iopodman.Volume) []*Volume {
   786  	var vols []*Volume
   787  	for _, v := range volumes {
   788  		volumeConfig := libpod.VolumeConfig{
   789  			Name:       v.Name,
   790  			Labels:     v.Labels,
   791  			MountPoint: v.MountPoint,
   792  			Driver:     v.Driver,
   793  			Options:    v.Options,
   794  		}
   795  		n := remoteVolume{
   796  			Runtime: r,
   797  			config:  &volumeConfig,
   798  		}
   799  		newVol := Volume{
   800  			n,
   801  		}
   802  		vols = append(vols, &newVol)
   803  	}
   804  	return vols
   805  }
   806  
   807  // PruneVolumes removes all unused volumes from the remote system
   808  func (r *LocalRuntime) PruneVolumes(ctx context.Context) ([]string, []error) {
   809  	var errs []error
   810  	prunedNames, prunedErrors, err := iopodman.VolumesPrune().Call(r.Conn)
   811  	if err != nil {
   812  		return []string{}, []error{err}
   813  	}
   814  	// We need to transform the string results of the error into actual error types
   815  	for _, e := range prunedErrors {
   816  		errs = append(errs, errors.New(e))
   817  	}
   818  	return prunedNames, errs
   819  }
   820  
   821  // SaveImage is a wrapper function for saving an image to the local filesystem
   822  func (r *LocalRuntime) SaveImage(ctx context.Context, c *cliconfig.SaveValues) error {
   823  	source := c.InputArgs[0]
   824  	additionalTags := c.InputArgs[1:]
   825  
   826  	options := iopodman.ImageSaveOptions{
   827  		Name:     source,
   828  		Format:   c.Format,
   829  		Output:   c.Output,
   830  		MoreTags: additionalTags,
   831  		Quiet:    c.Quiet,
   832  		Compress: c.Compress,
   833  	}
   834  	reply, err := iopodman.ImageSave().Send(r.Conn, varlink.More, options)
   835  	if err != nil {
   836  		return err
   837  	}
   838  
   839  	var fetchfile string
   840  	for {
   841  		responses, flags, err := reply()
   842  		if err != nil {
   843  			return err
   844  		}
   845  		if len(responses.Id) > 0 {
   846  			fetchfile = responses.Id
   847  		}
   848  		for _, line := range responses.Logs {
   849  			fmt.Print(line)
   850  		}
   851  		if flags&varlink.Continues == 0 {
   852  			break
   853  		}
   854  
   855  	}
   856  	if err != nil { // nolint: govet
   857  		return err
   858  	}
   859  
   860  	outputToDir := false
   861  	outfile := c.Output
   862  	var outputFile *os.File
   863  	// If the result is supposed to be a dir, then we need to put the tarfile
   864  	// from the host in a temporary file
   865  	if options.Format != "oci-archive" && options.Format != "docker-archive" {
   866  		outputToDir = true
   867  		outputFile, err = ioutil.TempFile("", "saveimage_tempfile")
   868  		if err != nil {
   869  			return err
   870  		}
   871  		outfile = outputFile.Name()
   872  		defer outputFile.Close()
   873  		defer os.Remove(outputFile.Name())
   874  	}
   875  	// We now need to fetch the tarball result back to the more system
   876  	if err := r.GetFileFromRemoteHost(fetchfile, outfile, true); err != nil {
   877  		return err
   878  	}
   879  
   880  	// If the result is a tarball, we're done
   881  	// If it is a dir, we need to untar the temporary file into the dir
   882  	if outputToDir {
   883  		if err := utils.UntarToFileSystem(c.Output, outputFile, &archive.TarOptions{}); err != nil {
   884  			return err
   885  		}
   886  	}
   887  	return nil
   888  }
   889  
   890  // LoadImage loads a container image from a remote client's filesystem
   891  func (r *LocalRuntime) LoadImage(ctx context.Context, name string, cli *cliconfig.LoadValues) (string, error) {
   892  	var names string
   893  	remoteTempFile, err := r.SendFileOverVarlink(cli.Input)
   894  	if err != nil {
   895  		return "", nil
   896  	}
   897  	more := varlink.More
   898  	if cli.Quiet {
   899  		more = 0
   900  	}
   901  	reply, err := iopodman.LoadImage().Send(r.Conn, uint64(more), name, remoteTempFile, cli.Quiet, true)
   902  	if err != nil {
   903  		return "", err
   904  	}
   905  
   906  	for {
   907  		responses, flags, err := reply()
   908  		if err != nil {
   909  			logrus.Error(err)
   910  			return "", err
   911  		}
   912  		for _, line := range responses.Logs {
   913  			fmt.Print(line)
   914  		}
   915  		names = responses.Id
   916  		if flags&varlink.Continues == 0 {
   917  			break
   918  		}
   919  	}
   920  	return names, nil
   921  }
   922  
   923  // IsImageNotFound checks if the error indicates that no image was found.
   924  func IsImageNotFound(err error) bool {
   925  	if errors.Cause(err) == image.ErrNoSuchImage {
   926  		return true
   927  	}
   928  	switch err.(type) { // nolint: gocritic
   929  	case *iopodman.ImageNotFound:
   930  		return true
   931  	}
   932  	return false
   933  }
   934  
   935  // HealthCheck executes a container's healthcheck over a varlink connection
   936  func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (string, error) {
   937  	return iopodman.HealthCheckRun().Call(r.Conn, c.InputArgs[0])
   938  }
   939  
   940  // Events monitors libpod/podman events over a varlink connection
   941  func (r *LocalRuntime) Events(c *cliconfig.EventValues) error {
   942  	var more uint64
   943  	if c.Stream {
   944  		more = uint64(varlink.More)
   945  	}
   946  	reply, err := iopodman.GetEvents().Send(r.Conn, more, c.Filter, c.Since, c.Until)
   947  	if err != nil {
   948  		return errors.Wrapf(err, "unable to obtain events")
   949  	}
   950  
   951  	w := bufio.NewWriter(os.Stdout)
   952  	var tmpl *template.Template
   953  	if c.Format != formats.JSONString {
   954  		template, err := template.New("events").Parse(c.Format)
   955  		if err != nil {
   956  			return err
   957  		}
   958  		tmpl = template
   959  	}
   960  
   961  	for {
   962  		returnedEvent, flags, err := reply()
   963  		if err != nil {
   964  			// When the error handling is back into podman, we can flip this to a better way to check
   965  			// for problems. For now, this works.
   966  			return err
   967  		}
   968  		if returnedEvent.Time == "" && returnedEvent.Status == "" && returnedEvent.Type == "" {
   969  			// We got a blank event return, signals end of stream in certain cases
   970  			break
   971  		}
   972  		eTime, err := time.Parse(time.RFC3339Nano, returnedEvent.Time)
   973  		if err != nil {
   974  			return errors.Wrapf(err, "unable to parse time of event %s", returnedEvent.Time)
   975  		}
   976  		eType, err := events.StringToType(returnedEvent.Type)
   977  		if err != nil {
   978  			return err
   979  		}
   980  		eStatus, err := events.StringToStatus(returnedEvent.Status)
   981  		if err != nil {
   982  			return err
   983  		}
   984  		event := events.Event{
   985  			ID:     returnedEvent.Id,
   986  			Image:  returnedEvent.Image,
   987  			Name:   returnedEvent.Name,
   988  			Status: eStatus,
   989  			Time:   eTime,
   990  			Type:   eType,
   991  		}
   992  		if c.Format == formats.JSONString { // nolint: gocritic
   993  			jsonStr, err := event.ToJSONString()
   994  			if err != nil {
   995  				return errors.Wrapf(err, "unable to format json")
   996  			}
   997  			if _, err := w.Write([]byte(jsonStr)); err != nil {
   998  				return err
   999  			}
  1000  		} else if len(c.Format) > 0 {
  1001  			if err := tmpl.Execute(w, event); err != nil {
  1002  				return err
  1003  			}
  1004  		} else {
  1005  			if _, err := w.Write([]byte(event.ToHumanReadable())); err != nil {
  1006  				return err
  1007  			}
  1008  		}
  1009  
  1010  		if _, err := w.Write([]byte("\n")); err != nil {
  1011  			return err
  1012  		}
  1013  		if err := w.Flush(); err != nil {
  1014  			return err
  1015  		}
  1016  		if flags&varlink.Continues == 0 {
  1017  			break
  1018  		}
  1019  	}
  1020  	return nil
  1021  }
  1022  
  1023  // Diff ...
  1024  func (r *LocalRuntime) Diff(c *cliconfig.DiffValues, to string) ([]archive.Change, error) {
  1025  	var changes []archive.Change
  1026  	reply, err := iopodman.Diff().Call(r.Conn, to)
  1027  	if err != nil {
  1028  		return nil, err
  1029  	}
  1030  	for _, change := range reply {
  1031  		changes = append(changes, archive.Change{Path: change.Path, Kind: stringToChangeType(change.ChangeType)})
  1032  	}
  1033  	return changes, nil
  1034  }
  1035  
  1036  func stringToChangeType(change string) archive.ChangeType {
  1037  	switch change {
  1038  	case "A":
  1039  		return archive.ChangeAdd
  1040  	case "D":
  1041  		return archive.ChangeDelete
  1042  	default: // nolint: gocritic,stylecheck
  1043  		logrus.Errorf("'%s' is unknown archive type", change)
  1044  		fallthrough
  1045  	case "C":
  1046  		return archive.ChangeModify
  1047  	}
  1048  }
  1049  
  1050  // GenerateKube creates kubernetes email from containers and pods
  1051  func (r *LocalRuntime) GenerateKube(c *cliconfig.GenerateKubeValues) (*v1.Pod, *v1.Service, error) {
  1052  	var (
  1053  		pod     v1.Pod
  1054  		service v1.Service
  1055  	)
  1056  	reply, err := iopodman.GenerateKube().Call(r.Conn, c.InputArgs[0], c.Service)
  1057  	if err != nil {
  1058  		return nil, nil, errors.Wrap(err, "unable to create kubernetes YAML")
  1059  	}
  1060  	if err := json.Unmarshal([]byte(reply.Pod), &pod); err != nil {
  1061  		return nil, nil, err
  1062  	}
  1063  	err = json.Unmarshal([]byte(reply.Service), &service)
  1064  	return &pod, &service, err
  1065  }
  1066  
  1067  // GetContainersByContext looks up containers based on the cli input of all, latest, or a list
  1068  func (r *LocalRuntime) GetContainersByContext(all bool, latest bool, namesOrIDs []string) ([]*Container, error) {
  1069  	var containers []*Container
  1070  	cids, err := iopodman.GetContainersByContext().Call(r.Conn, all, latest, namesOrIDs)
  1071  	if err != nil {
  1072  		return nil, err
  1073  	}
  1074  	for _, cid := range cids {
  1075  		ctr, err := r.LookupContainer(cid)
  1076  		if err != nil {
  1077  			return nil, err
  1078  		}
  1079  		containers = append(containers, ctr)
  1080  	}
  1081  	return containers, nil
  1082  }
  1083  
  1084  // GetVersion returns version information from service
  1085  func (r *LocalRuntime) GetVersion() (define.Version, error) {
  1086  	version, goVersion, gitCommit, built, osArch, apiVersion, err := iopodman.GetVersion().Call(r.Conn)
  1087  	if err != nil {
  1088  		return define.Version{}, errors.Wrapf(err, "Unable to obtain server version information")
  1089  	}
  1090  
  1091  	var buildTime int64
  1092  	if built != "" {
  1093  		t, err := time.Parse(time.RFC3339, built)
  1094  		if err != nil {
  1095  			return define.Version{}, nil
  1096  		}
  1097  		buildTime = t.Unix()
  1098  	}
  1099  
  1100  	return define.Version{
  1101  		RemoteAPIVersion: apiVersion,
  1102  		Version:          version,
  1103  		GoVersion:        goVersion,
  1104  		GitCommit:        gitCommit,
  1105  		Built:            buildTime,
  1106  		OsArch:           osArch,
  1107  	}, nil
  1108  }