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

     1  package tunnel
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"strconv"
     9  	"strings"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/containers/common/pkg/config"
    14  	"github.com/containers/image/v5/docker/reference"
    15  	"github.com/hanks177/podman/v4/libpod/define"
    16  	"github.com/hanks177/podman/v4/libpod/events"
    17  	"github.com/hanks177/podman/v4/pkg/api/handlers"
    18  	"github.com/hanks177/podman/v4/pkg/bindings/containers"
    19  	"github.com/hanks177/podman/v4/pkg/bindings/images"
    20  	"github.com/hanks177/podman/v4/pkg/domain/entities"
    21  	"github.com/hanks177/podman/v4/pkg/domain/entities/reports"
    22  	"github.com/hanks177/podman/v4/pkg/errorhandling"
    23  	"github.com/hanks177/podman/v4/pkg/specgen"
    24  	"github.com/hanks177/podman/v4/pkg/util"
    25  	"github.com/containers/storage/types"
    26  	"github.com/pkg/errors"
    27  	"github.com/sirupsen/logrus"
    28  )
    29  
    30  func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string, image string, args []string, options entities.ContainerRunlabelOptions) error {
    31  	return errors.New("not implemented")
    32  }
    33  
    34  func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string, options entities.ContainerExistsOptions) (*entities.BoolReport, error) {
    35  	exists, err := containers.Exists(ic.ClientCtx, nameOrID, new(containers.ExistsOptions).WithExternal(options.External))
    36  	return &entities.BoolReport{Value: exists}, err
    37  }
    38  
    39  func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, opts entities.WaitOptions) ([]entities.WaitReport, error) {
    40  	cons, err := getContainersByContext(ic.ClientCtx, false, false, namesOrIds)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	responses := make([]entities.WaitReport, 0, len(cons))
    45  	options := new(containers.WaitOptions).WithCondition(opts.Condition).WithInterval(opts.Interval.String())
    46  	for _, c := range cons {
    47  		response := entities.WaitReport{Id: c.ID}
    48  		exitCode, err := containers.Wait(ic.ClientCtx, c.ID, options)
    49  		if err != nil {
    50  			response.Error = err
    51  		} else {
    52  			response.ExitCode = exitCode
    53  		}
    54  		responses = append(responses, response)
    55  	}
    56  	return responses, nil
    57  }
    58  
    59  func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) {
    60  	ctrs, err := getContainersByContext(ic.ClientCtx, options.All, false, namesOrIds)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	reports := make([]*entities.PauseUnpauseReport, 0, len(ctrs))
    65  	for _, c := range ctrs {
    66  		err := containers.Pause(ic.ClientCtx, c.ID, nil)
    67  		if err != nil && options.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() {
    68  			logrus.Debugf("Container %s is not running", c.ID)
    69  			continue
    70  		}
    71  		reports = append(reports, &entities.PauseUnpauseReport{Id: c.ID, Err: err})
    72  	}
    73  	return reports, nil
    74  }
    75  
    76  func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) {
    77  	reports := []*entities.PauseUnpauseReport{}
    78  	ctrs, err := getContainersByContext(ic.ClientCtx, options.All, false, namesOrIds)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	for _, c := range ctrs {
    83  		err := containers.Unpause(ic.ClientCtx, c.ID, nil)
    84  		if err != nil && options.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() {
    85  			logrus.Debugf("Container %s is not paused", c.ID)
    86  			continue
    87  		}
    88  		reports = append(reports, &entities.PauseUnpauseReport{Id: c.ID, Err: err})
    89  	}
    90  	return reports, nil
    91  }
    92  
    93  func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, opts entities.StopOptions) ([]*entities.StopReport, error) {
    94  	reports := []*entities.StopReport{}
    95  	ctrs, rawInputs, err := getContainersAndInputByContext(ic.ClientCtx, opts.All, opts.Ignore, namesOrIds)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	ctrMap := map[string]string{}
   100  	for i := range ctrs {
   101  		ctrMap[ctrs[i].ID] = rawInputs[i]
   102  	}
   103  	options := new(containers.StopOptions).WithIgnore(opts.Ignore)
   104  	if to := opts.Timeout; to != nil {
   105  		options.WithTimeout(*to)
   106  	}
   107  	for _, c := range ctrs {
   108  		report := entities.StopReport{
   109  			Id:       c.ID,
   110  			RawInput: ctrMap[c.ID],
   111  		}
   112  		if err = containers.Stop(ic.ClientCtx, c.ID, options); err != nil {
   113  			// These first two are considered non-fatal under the right conditions
   114  			if errors.Cause(err).Error() == define.ErrCtrStopped.Error() {
   115  				logrus.Debugf("Container %s is already stopped", c.ID)
   116  				reports = append(reports, &report)
   117  				continue
   118  			} else if opts.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() {
   119  				logrus.Debugf("Container %s is not running, could not stop", c.ID)
   120  				reports = append(reports, &report)
   121  				continue
   122  			}
   123  
   124  			// TODO we need to associate errors returned by http with common
   125  			// define.errors so that we can equity tests. this will allow output
   126  			// to be the same as the native client
   127  			report.Err = err
   128  			reports = append(reports, &report)
   129  			continue
   130  		}
   131  		reports = append(reports, &report)
   132  	}
   133  	return reports, nil
   134  }
   135  
   136  func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, opts entities.KillOptions) ([]*entities.KillReport, error) {
   137  	ctrs, rawInputs, err := getContainersAndInputByContext(ic.ClientCtx, opts.All, false, namesOrIds)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	ctrMap := map[string]string{}
   142  	for i := range ctrs {
   143  		ctrMap[ctrs[i].ID] = rawInputs[i]
   144  	}
   145  	options := new(containers.KillOptions).WithSignal(opts.Signal)
   146  	reports := make([]*entities.KillReport, 0, len(ctrs))
   147  	for _, c := range ctrs {
   148  		err := containers.Kill(ic.ClientCtx, c.ID, options)
   149  		if err != nil && opts.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() {
   150  			logrus.Debugf("Container %s is not running", c.ID)
   151  			continue
   152  		}
   153  		reports = append(reports, &entities.KillReport{
   154  			Id:       c.ID,
   155  			Err:      err,
   156  			RawInput: ctrMap[c.ID],
   157  		})
   158  	}
   159  	return reports, nil
   160  }
   161  
   162  func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, opts entities.RestartOptions) ([]*entities.RestartReport, error) {
   163  	var (
   164  		reports = []*entities.RestartReport{}
   165  	)
   166  	options := new(containers.RestartOptions)
   167  	if to := opts.Timeout; to != nil {
   168  		options.WithTimeout(int(*to))
   169  	}
   170  	ctrs, err := getContainersByContext(ic.ClientCtx, opts.All, false, namesOrIds)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  	for _, c := range ctrs {
   175  		if opts.Running && c.State != define.ContainerStateRunning.String() {
   176  			continue
   177  		}
   178  		reports = append(reports, &entities.RestartReport{
   179  			Id:  c.ID,
   180  			Err: containers.Restart(ic.ClientCtx, c.ID, options),
   181  		})
   182  	}
   183  	return reports, nil
   184  }
   185  
   186  func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, opts entities.RmOptions) ([]*reports.RmReport, error) {
   187  	// TODO there is no endpoint for container eviction.  Need to discuss
   188  	options := new(containers.RemoveOptions).WithForce(opts.Force).WithVolumes(opts.Volumes).WithIgnore(opts.Ignore).WithDepend(opts.Depend)
   189  	if opts.Timeout != nil {
   190  		options = options.WithTimeout(*opts.Timeout)
   191  	}
   192  
   193  	toRemove := []string{}
   194  	alreadyRemoved := make(map[string]bool) // Avoids trying to remove already removed containers
   195  	if opts.All {
   196  		ctrs, err := getContainersByContext(ic.ClientCtx, opts.All, opts.Ignore, nil)
   197  		if err != nil {
   198  			return nil, err
   199  		}
   200  		for _, c := range ctrs {
   201  			toRemove = append(toRemove, c.ID)
   202  		}
   203  	} else {
   204  		for _, ctr := range namesOrIds {
   205  			// NOTE that we set ignore=true here to support
   206  			// removing external containers (e.g., Buildah
   207  			// containers). If we don't find the container,
   208  			// we'll use the raw input provided by the user
   209  			// instead of the ID. Since this can only happen
   210  			// with external containers, it poses no threat
   211  			// to the `alreadyRemoved` checks below.
   212  			ctrs, err := getContainersByContext(ic.ClientCtx, false, true, []string{ctr})
   213  			if err != nil {
   214  				return nil, err
   215  			}
   216  			id := ctr
   217  			if len(ctrs) == 1 {
   218  				id = ctrs[0].ID
   219  			}
   220  			toRemove = append(toRemove, id)
   221  		}
   222  	}
   223  
   224  	rmReports := make([]*reports.RmReport, 0, len(toRemove))
   225  	for _, nameOrID := range toRemove {
   226  		if alreadyRemoved[nameOrID] {
   227  			continue
   228  		}
   229  		newReports, err := containers.Remove(ic.ClientCtx, nameOrID, options)
   230  		if err != nil {
   231  			rmReports = append(rmReports, &reports.RmReport{Id: nameOrID, Err: err})
   232  			continue
   233  		}
   234  		for i := range newReports {
   235  			alreadyRemoved[newReports[i].Id] = true
   236  			rmReports = append(rmReports, newReports[i])
   237  		}
   238  	}
   239  	return rmReports, nil
   240  }
   241  
   242  func (ic *ContainerEngine) ContainerPrune(ctx context.Context, opts entities.ContainerPruneOptions) ([]*reports.PruneReport, error) {
   243  	options := new(containers.PruneOptions).WithFilters(opts.Filters)
   244  	return containers.Prune(ic.ClientCtx, options)
   245  }
   246  
   247  func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, opts entities.InspectOptions) ([]*entities.ContainerInspectReport, []error, error) {
   248  	var (
   249  		reports = make([]*entities.ContainerInspectReport, 0, len(namesOrIds))
   250  		errs    = []error{}
   251  	)
   252  	options := new(containers.InspectOptions).WithSize(opts.Size)
   253  	for _, name := range namesOrIds {
   254  		inspect, err := containers.Inspect(ic.ClientCtx, name, options)
   255  		if err != nil {
   256  			errModel, ok := err.(*errorhandling.ErrorModel)
   257  			if !ok {
   258  				return nil, nil, err
   259  			}
   260  			if errModel.ResponseCode == 404 {
   261  				errs = append(errs, errors.Errorf("no such container %q", name))
   262  				continue
   263  			}
   264  			return nil, nil, err
   265  		}
   266  		reports = append(reports, &entities.ContainerInspectReport{InspectContainerData: inspect})
   267  	}
   268  	return reports, errs, nil
   269  }
   270  
   271  func (ic *ContainerEngine) ContainerTop(ctx context.Context, opts entities.TopOptions) (*entities.StringSliceReport, error) {
   272  	switch {
   273  	case opts.Latest:
   274  		return nil, errors.New("latest is not supported")
   275  	case opts.NameOrID == "":
   276  		return nil, errors.New("NameOrID must be specified")
   277  	}
   278  	options := new(containers.TopOptions).WithDescriptors(opts.Descriptors)
   279  	topOutput, err := containers.Top(ic.ClientCtx, opts.NameOrID, options)
   280  	if err != nil {
   281  		return nil, err
   282  	}
   283  	return &entities.StringSliceReport{Value: topOutput}, nil
   284  }
   285  
   286  func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, opts entities.CommitOptions) (*entities.CommitReport, error) {
   287  	var (
   288  		repo string
   289  		tag  = "latest"
   290  	)
   291  	if len(opts.ImageName) > 0 {
   292  		ref, err := reference.Parse(opts.ImageName)
   293  		if err != nil {
   294  			return nil, errors.Wrapf(err, "error parsing reference %q", opts.ImageName)
   295  		}
   296  		if t, ok := ref.(reference.Tagged); ok {
   297  			tag = t.Tag()
   298  		}
   299  		if r, ok := ref.(reference.Named); ok {
   300  			repo = r.Name()
   301  		}
   302  		if len(repo) < 1 {
   303  			return nil, errors.Errorf("invalid image name %q", opts.ImageName)
   304  		}
   305  	}
   306  	options := new(containers.CommitOptions).WithAuthor(opts.Author).WithChanges(opts.Changes).WithComment(opts.Message).WithSquash(opts.Squash)
   307  	options.WithFormat(opts.Format).WithPause(opts.Pause).WithRepo(repo).WithTag(tag)
   308  	response, err := containers.Commit(ic.ClientCtx, nameOrID, options)
   309  	if err != nil {
   310  		return nil, err
   311  	}
   312  	return &entities.CommitReport{Id: response.ID}, nil
   313  }
   314  
   315  func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string, options entities.ContainerExportOptions) error {
   316  	var (
   317  		err error
   318  		w   io.Writer
   319  	)
   320  	if len(options.Output) > 0 {
   321  		w, err = os.Create(options.Output)
   322  		if err != nil {
   323  			return err
   324  		}
   325  	}
   326  	return containers.Export(ic.ClientCtx, nameOrID, w, nil)
   327  }
   328  
   329  func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, opts entities.CheckpointOptions) ([]*entities.CheckpointReport, error) {
   330  	options := new(containers.CheckpointOptions)
   331  	options.WithFileLocks(opts.FileLocks)
   332  	options.WithIgnoreRootfs(opts.IgnoreRootFS)
   333  	options.WithKeep(opts.Keep)
   334  	options.WithExport(opts.Export)
   335  	options.WithCreateImage(opts.CreateImage)
   336  	options.WithTCPEstablished(opts.TCPEstablished)
   337  	options.WithPrintStats(opts.PrintStats)
   338  	options.WithPreCheckpoint(opts.PreCheckPoint)
   339  	options.WithLeaveRunning(opts.LeaveRunning)
   340  	options.WithWithPrevious(opts.WithPrevious)
   341  
   342  	var (
   343  		err  error
   344  		ctrs = []entities.ListContainer{}
   345  	)
   346  
   347  	if opts.All {
   348  		allCtrs, err := getContainersByContext(ic.ClientCtx, true, false, []string{})
   349  		if err != nil {
   350  			return nil, err
   351  		}
   352  		// narrow the list to running only
   353  		for _, c := range allCtrs {
   354  			if c.State == define.ContainerStateRunning.String() {
   355  				ctrs = append(ctrs, c)
   356  			}
   357  		}
   358  	} else {
   359  		ctrs, err = getContainersByContext(ic.ClientCtx, false, false, namesOrIds)
   360  		if err != nil {
   361  			return nil, err
   362  		}
   363  	}
   364  	reports := make([]*entities.CheckpointReport, 0, len(ctrs))
   365  	for _, c := range ctrs {
   366  		report, err := containers.Checkpoint(ic.ClientCtx, c.ID, options)
   367  		if err != nil {
   368  			reports = append(reports, &entities.CheckpointReport{Id: c.ID, Err: err})
   369  		} else {
   370  			reports = append(reports, report)
   371  		}
   372  	}
   373  	return reports, nil
   374  }
   375  
   376  func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, opts entities.RestoreOptions) ([]*entities.RestoreReport, error) {
   377  	if opts.ImportPrevious != "" {
   378  		return nil, fmt.Errorf("--import-previous is not supported on the remote client")
   379  	}
   380  
   381  	options := new(containers.RestoreOptions)
   382  	options.WithFileLocks(opts.FileLocks)
   383  	options.WithIgnoreRootfs(opts.IgnoreRootFS)
   384  	options.WithIgnoreVolumes(opts.IgnoreVolumes)
   385  	options.WithIgnoreStaticIP(opts.IgnoreStaticIP)
   386  	options.WithIgnoreStaticMAC(opts.IgnoreStaticMAC)
   387  	options.WithKeep(opts.Keep)
   388  	options.WithName(opts.Name)
   389  	options.WithTCPEstablished(opts.TCPEstablished)
   390  	options.WithPod(opts.Pod)
   391  	options.WithPrintStats(opts.PrintStats)
   392  	options.WithPublishPorts(opts.PublishPorts)
   393  
   394  	if opts.Import != "" {
   395  		options.WithImportArchive(opts.Import)
   396  		report, err := containers.Restore(ic.ClientCtx, "", options)
   397  		return []*entities.RestoreReport{report}, err
   398  	}
   399  
   400  	var (
   401  		ids = []string{}
   402  	)
   403  	if opts.All {
   404  		allCtrs, err := getContainersByContext(ic.ClientCtx, true, false, []string{})
   405  		if err != nil {
   406  			return nil, err
   407  		}
   408  		// narrow the list to exited only
   409  		for _, c := range allCtrs {
   410  			if c.State == define.ContainerStateExited.String() {
   411  				ids = append(ids, c.ID)
   412  			}
   413  		}
   414  	} else {
   415  		getImageOptions := new(images.GetOptions).WithSize(false)
   416  		hostInfo, err := ic.Info(context.Background())
   417  		if err != nil {
   418  			return nil, err
   419  		}
   420  
   421  		for _, nameOrID := range namesOrIds {
   422  			ctrData, _, err := ic.ContainerInspect(ic.ClientCtx, []string{nameOrID}, entities.InspectOptions{})
   423  			if err == nil && len(ctrData) > 0 {
   424  				ids = append(ids, ctrData[0].ID)
   425  			} else {
   426  				// If container was not found, check if this is a checkpoint image
   427  				inspectReport, err := images.GetImage(ic.ClientCtx, nameOrID, getImageOptions)
   428  				if err != nil {
   429  					return nil, fmt.Errorf("no such container or image: %s", nameOrID)
   430  				}
   431  				checkpointRuntimeName, found := inspectReport.Annotations[define.CheckpointAnnotationRuntimeName]
   432  				if !found {
   433  					return nil, fmt.Errorf("image is not a checkpoint: %s", nameOrID)
   434  				}
   435  				if hostInfo.Host.OCIRuntime.Name != checkpointRuntimeName {
   436  					return nil, fmt.Errorf("container image \"%s\" requires runtime: \"%s\"", nameOrID, checkpointRuntimeName)
   437  				}
   438  				ids = append(ids, inspectReport.ID)
   439  			}
   440  		}
   441  	}
   442  	reports := make([]*entities.RestoreReport, 0, len(ids))
   443  	for _, id := range ids {
   444  		report, err := containers.Restore(ic.ClientCtx, id, options)
   445  		if err != nil {
   446  			reports = append(reports, &entities.RestoreReport{Id: id, Err: err})
   447  		}
   448  		reports = append(reports, report)
   449  	}
   450  	return reports, nil
   451  }
   452  
   453  func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*entities.ContainerCreateReport, error) {
   454  	response, err := containers.CreateWithSpec(ic.ClientCtx, s, nil)
   455  	if err != nil {
   456  		return nil, err
   457  	}
   458  	for _, w := range response.Warnings {
   459  		fmt.Fprintf(os.Stderr, "%s\n", w)
   460  	}
   461  	return &entities.ContainerCreateReport{Id: response.ID}, nil
   462  }
   463  
   464  func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, opts entities.ContainerLogsOptions) error {
   465  	since := opts.Since.Format(time.RFC3339)
   466  	until := opts.Until.Format(time.RFC3339)
   467  	tail := strconv.FormatInt(opts.Tail, 10)
   468  	stdout := opts.StdoutWriter != nil
   469  	stderr := opts.StderrWriter != nil
   470  	options := new(containers.LogOptions).WithFollow(opts.Follow).WithSince(since).WithUntil(until).WithStderr(stderr)
   471  	options.WithStdout(stdout).WithTail(tail)
   472  
   473  	var err error
   474  	stdoutCh := make(chan string)
   475  	stderrCh := make(chan string)
   476  	ctx, cancel := context.WithCancel(context.Background())
   477  	go func() {
   478  		err = containers.Logs(ic.ClientCtx, nameOrIDs[0], options, stdoutCh, stderrCh)
   479  		cancel()
   480  	}()
   481  
   482  	for {
   483  		select {
   484  		case <-ctx.Done():
   485  			return err
   486  		case line := <-stdoutCh:
   487  			if opts.StdoutWriter != nil {
   488  				_, _ = io.WriteString(opts.StdoutWriter, line)
   489  			}
   490  		case line := <-stderrCh:
   491  			if opts.StderrWriter != nil {
   492  				_, _ = io.WriteString(opts.StderrWriter, line)
   493  			}
   494  		}
   495  	}
   496  }
   497  
   498  func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, opts entities.AttachOptions) error {
   499  	ctrs, err := getContainersByContext(ic.ClientCtx, false, false, []string{nameOrID})
   500  	if err != nil {
   501  		return err
   502  	}
   503  	ctr := ctrs[0]
   504  	if ctr.State != define.ContainerStateRunning.String() {
   505  		return errors.Errorf("you can only attach to running containers")
   506  	}
   507  	options := new(containers.AttachOptions).WithStream(true).WithDetachKeys(opts.DetachKeys)
   508  	return containers.Attach(ic.ClientCtx, nameOrID, opts.Stdin, opts.Stdout, opts.Stderr, nil, options)
   509  }
   510  
   511  func makeExecConfig(options entities.ExecOptions) *handlers.ExecCreateConfig {
   512  	env := []string{}
   513  	for k, v := range options.Envs {
   514  		env = append(env, fmt.Sprintf("%s=%s", k, v))
   515  	}
   516  
   517  	createConfig := new(handlers.ExecCreateConfig)
   518  	createConfig.User = options.User
   519  	createConfig.Privileged = options.Privileged
   520  	createConfig.Tty = options.Tty
   521  	createConfig.AttachStdin = options.Interactive
   522  	createConfig.AttachStdout = true
   523  	createConfig.AttachStderr = true
   524  	createConfig.Detach = false
   525  	createConfig.DetachKeys = options.DetachKeys
   526  	createConfig.Env = env
   527  	createConfig.WorkingDir = options.WorkDir
   528  	createConfig.Cmd = options.Cmd
   529  
   530  	return createConfig
   531  }
   532  
   533  func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrID string, options entities.ExecOptions, streams define.AttachStreams) (int, error) {
   534  	createConfig := makeExecConfig(options)
   535  
   536  	sessionID, err := containers.ExecCreate(ic.ClientCtx, nameOrID, createConfig)
   537  	if err != nil {
   538  		return 125, err
   539  	}
   540  	startAndAttachOptions := new(containers.ExecStartAndAttachOptions)
   541  	startAndAttachOptions.WithOutputStream(streams.OutputStream).WithErrorStream(streams.ErrorStream)
   542  	if streams.InputStream != nil {
   543  		startAndAttachOptions.WithInputStream(*streams.InputStream)
   544  	}
   545  	startAndAttachOptions.WithAttachError(streams.AttachError).WithAttachOutput(streams.AttachOutput).WithAttachInput(streams.AttachInput)
   546  	if err := containers.ExecStartAndAttach(ic.ClientCtx, sessionID, startAndAttachOptions); err != nil {
   547  		return 125, err
   548  	}
   549  
   550  	inspectOut, err := containers.ExecInspect(ic.ClientCtx, sessionID, nil)
   551  	if err != nil {
   552  		return 125, err
   553  	}
   554  
   555  	return inspectOut.ExitCode, nil
   556  }
   557  
   558  func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID string, options entities.ExecOptions) (string, error) {
   559  	createConfig := makeExecConfig(options)
   560  
   561  	sessionID, err := containers.ExecCreate(ic.ClientCtx, nameOrID, createConfig)
   562  	if err != nil {
   563  		return "", err
   564  	}
   565  
   566  	if err := containers.ExecStart(ic.ClientCtx, sessionID, nil); err != nil {
   567  		return "", err
   568  	}
   569  
   570  	return sessionID, nil
   571  }
   572  
   573  func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, output, errput *os.File) error { //nolint
   574  	attachErr := make(chan error)
   575  	attachReady := make(chan bool)
   576  	options := new(containers.AttachOptions).WithStream(true)
   577  	if dk := detachKeys; dk != nil {
   578  		options.WithDetachKeys(*dk)
   579  	}
   580  	go func() {
   581  		err := containers.Attach(ic.ClientCtx, name, input, output, errput, attachReady, options)
   582  		attachErr <- err
   583  	}()
   584  	// Wait for the attach to actually happen before starting
   585  	// the container.
   586  	select {
   587  	case <-attachReady:
   588  		startOptions := new(containers.StartOptions)
   589  		if dk := detachKeys; dk != nil {
   590  			startOptions.WithDetachKeys(*dk)
   591  		}
   592  		if err := containers.Start(ic.ClientCtx, name, startOptions); err != nil {
   593  			return err
   594  		}
   595  	case err := <-attachErr:
   596  		return err
   597  	}
   598  	// If attachReady happens first, wait for containers.Attach to complete
   599  	return <-attachErr
   600  }
   601  
   602  func logIfRmError(id string, err error, reports []*reports.RmReport) {
   603  	logError := func(id string, err error) {
   604  		if errorhandling.Contains(err, define.ErrNoSuchCtr) ||
   605  			errorhandling.Contains(err, define.ErrCtrRemoved) ||
   606  			errorhandling.Contains(err, types.ErrLayerUnknown) {
   607  			logrus.Debugf("Container %s does not exist: %v", id, err)
   608  		} else {
   609  			logrus.Errorf("Removing container %s: %v", id, err)
   610  		}
   611  	}
   612  	if err != nil {
   613  		logError(id, err)
   614  	} else {
   615  		for _, report := range reports {
   616  			if report.Err != nil {
   617  				logError(report.Id, report.Err)
   618  			}
   619  		}
   620  	}
   621  }
   622  
   623  func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) {
   624  	reports := []*entities.ContainerStartReport{}
   625  	var exitCode = define.ExecErrorCodeGeneric
   626  	containersNamesOrIds := namesOrIds
   627  	all := options.All
   628  	if len(options.Filters) > 0 {
   629  		all = false
   630  		containersNamesOrIds = []string{}
   631  		opts := new(containers.ListOptions).WithFilters(options.Filters).WithAll(true)
   632  		candidates, listErr := containers.List(ic.ClientCtx, opts)
   633  		if listErr != nil {
   634  			return nil, listErr
   635  		}
   636  		for _, candidate := range candidates {
   637  			if options.All {
   638  				containersNamesOrIds = append(containersNamesOrIds, candidate.ID)
   639  				continue
   640  			}
   641  			for _, nameOrID := range namesOrIds {
   642  				if nameOrID == candidate.ID {
   643  					containersNamesOrIds = append(containersNamesOrIds, nameOrID)
   644  					continue
   645  				}
   646  				for _, containerName := range candidate.Names {
   647  					if containerName == nameOrID {
   648  						containersNamesOrIds = append(containersNamesOrIds, nameOrID)
   649  						continue
   650  					}
   651  				}
   652  			}
   653  		}
   654  	}
   655  	ctrs, err := getContainersByContext(ic.ClientCtx, all, false, containersNamesOrIds)
   656  	if err != nil {
   657  		return nil, err
   658  	}
   659  	removeOptions := new(containers.RemoveOptions).WithVolumes(true).WithForce(false)
   660  	removeContainer := func(id string) {
   661  		reports, err := containers.Remove(ic.ClientCtx, id, removeOptions)
   662  		logIfRmError(id, err, reports)
   663  	}
   664  
   665  	// There can only be one container if attach was used
   666  	for i, ctr := range ctrs {
   667  		name := ctr.ID
   668  		rawInput := ctr.ID
   669  		if !options.All {
   670  			rawInput = namesOrIds[i]
   671  		}
   672  		report := entities.ContainerStartReport{
   673  			Id:       name,
   674  			RawInput: rawInput,
   675  			ExitCode: exitCode,
   676  		}
   677  		ctrRunning := ctr.State == define.ContainerStateRunning.String()
   678  		if options.Attach {
   679  			err = startAndAttach(ic, name, &options.DetachKeys, options.Stdin, options.Stdout, options.Stderr)
   680  			if err == define.ErrDetach {
   681  				// User manually detached
   682  				// Exit cleanly immediately
   683  				reports = append(reports, &report)
   684  				return reports, nil
   685  			}
   686  			if ctrRunning {
   687  				reports = append(reports, &report)
   688  				return reports, nil
   689  			}
   690  
   691  			if err != nil {
   692  				if ctr.AutoRemove {
   693  					removeContainer(ctr.ID)
   694  				}
   695  				report.ExitCode = define.ExitCode(report.Err)
   696  				report.Err = err
   697  				reports = append(reports, &report)
   698  				return reports, errors.Wrapf(report.Err, "unable to start container %s", name)
   699  			}
   700  			if ctr.AutoRemove {
   701  				// Defer the removal, so we can return early if needed and
   702  				// de-spaghetti the code.
   703  				defer func() {
   704  					shouldRestart, err := containers.ShouldRestart(ic.ClientCtx, ctr.ID, nil)
   705  					if err != nil {
   706  						logrus.Errorf("Failed to check if %s should restart: %v", ctr.ID, err)
   707  						return
   708  					}
   709  					logrus.Errorf("Should restart: %v", shouldRestart)
   710  
   711  					if !shouldRestart && ctr.AutoRemove {
   712  						removeContainer(ctr.ID)
   713  					}
   714  				}()
   715  			}
   716  
   717  			exitCode, err := containers.Wait(ic.ClientCtx, name, nil)
   718  			if err == define.ErrNoSuchCtr {
   719  				// Check events
   720  				event, err := ic.GetLastContainerEvent(ctx, name, events.Exited)
   721  				if err != nil {
   722  					logrus.Errorf("Cannot get exit code: %v", err)
   723  					report.ExitCode = define.ExecErrorCodeNotFound
   724  				} else {
   725  					report.ExitCode = event.ContainerExitCode
   726  				}
   727  			} else {
   728  				report.ExitCode = int(exitCode)
   729  			}
   730  			reports = append(reports, &report)
   731  			return reports, nil
   732  		}
   733  		// Start the container if it's not running already.
   734  		if !ctrRunning {
   735  			err = containers.Start(ic.ClientCtx, name, new(containers.StartOptions).WithDetachKeys(options.DetachKeys))
   736  			if err != nil {
   737  				if ctr.AutoRemove {
   738  					rmOptions := new(containers.RemoveOptions).WithForce(false).WithVolumes(true)
   739  					reports, err := containers.Remove(ic.ClientCtx, ctr.ID, rmOptions)
   740  					logIfRmError(ctr.ID, err, reports)
   741  				}
   742  				report.Err = errors.Wrapf(err, "unable to start container %q", name)
   743  				report.ExitCode = define.ExitCode(err)
   744  				reports = append(reports, &report)
   745  				continue
   746  			}
   747  			report.ExitCode = 0
   748  			reports = append(reports, &report)
   749  		}
   750  	}
   751  	return reports, nil
   752  }
   753  
   754  func (ic *ContainerEngine) ContainerList(ctx context.Context, opts entities.ContainerListOptions) ([]entities.ListContainer, error) {
   755  	options := new(containers.ListOptions).WithFilters(opts.Filters).WithAll(opts.All).WithLast(opts.Last)
   756  	options.WithNamespace(opts.Namespace).WithSize(opts.Size).WithSync(opts.Sync).WithExternal(opts.External)
   757  	return containers.List(ic.ClientCtx, options)
   758  }
   759  
   760  func (ic *ContainerEngine) ContainerListExternal(ctx context.Context) ([]entities.ListContainer, error) {
   761  	options := new(containers.ListOptions).WithAll(true)
   762  	options.WithNamespace(true).WithSize(true).WithSync(true).WithExternal(true)
   763  	return containers.List(ic.ClientCtx, options)
   764  }
   765  
   766  func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) {
   767  	con, err := containers.CreateWithSpec(ic.ClientCtx, opts.Spec, nil)
   768  	if err != nil {
   769  		return nil, err
   770  	}
   771  	for _, w := range con.Warnings {
   772  		fmt.Fprintf(os.Stderr, "%s\n", w)
   773  	}
   774  	if opts.CIDFile != "" {
   775  		if err := util.CreateCidFile(opts.CIDFile, con.ID); err != nil {
   776  			return nil, err
   777  		}
   778  	}
   779  
   780  	report := entities.ContainerRunReport{Id: con.ID}
   781  
   782  	if opts.Detach {
   783  		// Detach and return early
   784  		err := containers.Start(ic.ClientCtx, con.ID, new(containers.StartOptions).WithRecursive(true))
   785  		if err != nil {
   786  			report.ExitCode = define.ExitCode(err)
   787  		}
   788  		return &report, err
   789  	}
   790  
   791  	// Attach
   792  	if err := startAndAttach(ic, con.ID, &opts.DetachKeys, opts.InputStream, opts.OutputStream, opts.ErrorStream); err != nil {
   793  		if err == define.ErrDetach {
   794  			return &report, nil
   795  		}
   796  
   797  		report.ExitCode = define.ExitCode(err)
   798  		if opts.Rm {
   799  			reports, rmErr := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true))
   800  			if rmErr != nil || reports[0].Err != nil {
   801  				logrus.Debugf("unable to remove container %s after failing to start and attach to it", con.ID)
   802  			}
   803  		}
   804  		return &report, err
   805  	}
   806  
   807  	if opts.Rm {
   808  		// Defer the removal, so we can return early if needed and
   809  		// de-spaghetti the code.
   810  		defer func() {
   811  			shouldRestart, err := containers.ShouldRestart(ic.ClientCtx, con.ID, nil)
   812  			if err != nil {
   813  				logrus.Errorf("Failed to check if %s should restart: %v", con.ID, err)
   814  				return
   815  			}
   816  
   817  			if !shouldRestart {
   818  				reports, err := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true))
   819  				logIfRmError(con.ID, err, reports)
   820  			}
   821  		}()
   822  	}
   823  
   824  	// Wait
   825  	exitCode, waitErr := containers.Wait(ic.ClientCtx, con.ID, nil)
   826  	if waitErr == nil {
   827  		report.ExitCode = int(exitCode)
   828  		return &report, nil
   829  	}
   830  
   831  	// Determine why the wait failed.  If the container doesn't exist,
   832  	// consult the events.
   833  	if !errorhandling.Contains(waitErr, define.ErrNoSuchCtr) {
   834  		return &report, waitErr
   835  	}
   836  
   837  	// Events
   838  	eventsChannel := make(chan *events.Event)
   839  	eventOptions := entities.EventsOptions{
   840  		EventChan: eventsChannel,
   841  		Filter: []string{
   842  			"type=container",
   843  			fmt.Sprintf("container=%s", con.ID),
   844  			fmt.Sprintf("event=%s", events.Exited),
   845  		},
   846  	}
   847  
   848  	var lastEvent *events.Event
   849  	var mutex sync.Mutex
   850  	mutex.Lock()
   851  	// Read the events.
   852  	go func() {
   853  		for e := range eventsChannel {
   854  			lastEvent = e
   855  		}
   856  		mutex.Unlock()
   857  	}()
   858  
   859  	eventsErr := ic.Events(ctx, eventOptions)
   860  
   861  	// Wait for all events to be read
   862  	mutex.Lock()
   863  	if eventsErr != nil || lastEvent == nil {
   864  		logrus.Errorf("Cannot get exit code: %v", err)
   865  		report.ExitCode = define.ExecErrorCodeNotFound
   866  		return &report, nil // nolint: nilerr
   867  	}
   868  
   869  	report.ExitCode = lastEvent.ContainerExitCode
   870  	return &report, err
   871  }
   872  
   873  func (ic *ContainerEngine) Diff(ctx context.Context, namesOrIDs []string, opts entities.DiffOptions) (*entities.DiffReport, error) {
   874  	var base string
   875  	options := new(containers.DiffOptions).WithDiffType(opts.Type.String())
   876  	if len(namesOrIDs) > 0 {
   877  		base = namesOrIDs[0]
   878  		if len(namesOrIDs) > 1 {
   879  			options.WithParent(namesOrIDs[1])
   880  		}
   881  	} else {
   882  		return nil, errors.New("no arguments for diff")
   883  	}
   884  	changes, err := containers.Diff(ic.ClientCtx, base, options)
   885  	return &entities.DiffReport{Changes: changes}, err
   886  }
   887  
   888  func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []string, options entities.ContainerCleanupOptions) ([]*entities.ContainerCleanupReport, error) {
   889  	return nil, errors.New("not implemented")
   890  }
   891  
   892  func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []string, options entities.ContainerInitOptions) ([]*entities.ContainerInitReport, error) {
   893  	ctrs, err := getContainersByContext(ic.ClientCtx, options.All, false, namesOrIds)
   894  	if err != nil {
   895  		return nil, err
   896  	}
   897  	reports := make([]*entities.ContainerInitReport, 0, len(ctrs))
   898  	for _, ctr := range ctrs {
   899  		err := containers.ContainerInit(ic.ClientCtx, ctr.ID, nil)
   900  		// When using all, it is NOT considered an error if a container
   901  		// has already been init'd.
   902  		if err != nil && options.All && strings.Contains(errors.Cause(err).Error(), define.ErrCtrStateInvalid.Error()) {
   903  			err = nil
   904  		}
   905  		reports = append(reports, &entities.ContainerInitReport{
   906  			Err: err,
   907  			Id:  ctr.ID,
   908  		})
   909  	}
   910  	return reports, nil
   911  }
   912  
   913  func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []string, options entities.ContainerMountOptions) ([]*entities.ContainerMountReport, error) {
   914  	return nil, errors.New("mounting containers is not supported for remote clients")
   915  }
   916  
   917  func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIDs []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) {
   918  	return nil, errors.New("unmounting containers is not supported for remote clients")
   919  }
   920  
   921  func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) {
   922  	return config.Default()
   923  }
   924  
   925  func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) {
   926  	var (
   927  		reports    = []*entities.ContainerPortReport{}
   928  		namesOrIds = []string{}
   929  	)
   930  	if len(nameOrID) > 0 {
   931  		namesOrIds = append(namesOrIds, nameOrID)
   932  	}
   933  	ctrs, err := getContainersByContext(ic.ClientCtx, options.All, false, namesOrIds)
   934  	if err != nil {
   935  		return nil, err
   936  	}
   937  	for _, con := range ctrs {
   938  		if con.State != define.ContainerStateRunning.String() {
   939  			continue
   940  		}
   941  		if len(con.Ports) > 0 {
   942  			reports = append(reports, &entities.ContainerPortReport{
   943  				Id:    con.ID,
   944  				Ports: con.Ports,
   945  			})
   946  		}
   947  	}
   948  	return reports, nil
   949  }
   950  
   951  func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID, path string, reader io.Reader, options entities.CopyOptions) (entities.ContainerCopyFunc, error) {
   952  	copyOptions := new(containers.CopyOptions).WithChown(options.Chown).WithRename(options.Rename)
   953  	return containers.CopyFromArchiveWithOptions(ic.ClientCtx, nameOrID, path, reader, copyOptions)
   954  }
   955  
   956  func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (entities.ContainerCopyFunc, error) {
   957  	return containers.CopyToArchive(ic.ClientCtx, nameOrID, path, writer)
   958  }
   959  
   960  func (ic *ContainerEngine) ContainerStat(ctx context.Context, nameOrID string, path string) (*entities.ContainerStatReport, error) {
   961  	return containers.Stat(ic.ClientCtx, nameOrID, path)
   962  }
   963  
   964  // Shutdown Libpod engine.
   965  func (ic *ContainerEngine) Shutdown(_ context.Context) {
   966  }
   967  
   968  func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []string, options entities.ContainerStatsOptions) (statsChan chan entities.ContainerStatsReport, err error) {
   969  	if options.Latest {
   970  		return nil, errors.New("latest is not supported for the remote client")
   971  	}
   972  	return containers.Stats(ic.ClientCtx, namesOrIds, new(containers.StatsOptions).WithStream(options.Stream).WithInterval(options.Interval))
   973  }
   974  
   975  // ShouldRestart reports back whether the container will restart.
   976  func (ic *ContainerEngine) ShouldRestart(_ context.Context, id string) (bool, error) {
   977  	return containers.ShouldRestart(ic.ClientCtx, id, nil)
   978  }
   979  
   980  // ContainerRename renames the given container.
   981  func (ic *ContainerEngine) ContainerRename(ctx context.Context, nameOrID string, opts entities.ContainerRenameOptions) error {
   982  	return containers.Rename(ic.ClientCtx, nameOrID, new(containers.RenameOptions).WithName(opts.NewName))
   983  }
   984  
   985  func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts entities.ContainerCloneOptions) (*entities.ContainerCreateReport, error) {
   986  	return nil, errors.New("cloning a container is not supported on the remote client")
   987  }