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

     1  package tunnel
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"strconv"
    10  	"strings"
    11  	"sync"
    12  	"time"
    13  
    14  	"github.com/containers/common/pkg/config"
    15  	"github.com/containers/image/v5/docker/reference"
    16  	"github.com/containers/podman/v2/libpod/define"
    17  	"github.com/containers/podman/v2/libpod/events"
    18  	"github.com/containers/podman/v2/pkg/api/handlers"
    19  	"github.com/containers/podman/v2/pkg/bindings"
    20  	"github.com/containers/podman/v2/pkg/bindings/containers"
    21  	"github.com/containers/podman/v2/pkg/domain/entities"
    22  	"github.com/containers/podman/v2/pkg/errorhandling"
    23  	"github.com/containers/podman/v2/pkg/specgen"
    24  	"github.com/containers/podman/v2/pkg/util"
    25  	"github.com/pkg/errors"
    26  	"github.com/sirupsen/logrus"
    27  )
    28  
    29  func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string, image string, args []string, options entities.ContainerRunlabelOptions) error {
    30  	return errors.New("not implemented")
    31  }
    32  
    33  func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string, options entities.ContainerExistsOptions) (*entities.BoolReport, error) {
    34  	exists, err := containers.Exists(ic.ClientCxt, nameOrID, options.External)
    35  	return &entities.BoolReport{Value: exists}, err
    36  }
    37  
    38  func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) {
    39  	cons, err := getContainersByContext(ic.ClientCxt, false, false, namesOrIds)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	responses := make([]entities.WaitReport, 0, len(cons))
    44  	for _, c := range cons {
    45  		response := entities.WaitReport{Id: c.ID}
    46  		exitCode, err := containers.Wait(ic.ClientCxt, c.ID, &options.Condition)
    47  		if err != nil {
    48  			response.Error = err
    49  		} else {
    50  			response.ExitCode = exitCode
    51  		}
    52  		responses = append(responses, response)
    53  	}
    54  	return responses, nil
    55  }
    56  
    57  func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) {
    58  	ctrs, err := getContainersByContext(ic.ClientCxt, options.All, false, namesOrIds)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	reports := make([]*entities.PauseUnpauseReport, 0, len(ctrs))
    63  	for _, c := range ctrs {
    64  		err := containers.Pause(ic.ClientCxt, c.ID)
    65  		reports = append(reports, &entities.PauseUnpauseReport{Id: c.ID, Err: err})
    66  	}
    67  	return reports, nil
    68  }
    69  
    70  func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) {
    71  	ctrs, err := getContainersByContext(ic.ClientCxt, options.All, false, namesOrIds)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	reports := make([]*entities.PauseUnpauseReport, 0, len(ctrs))
    76  	for _, c := range ctrs {
    77  		err := containers.Unpause(ic.ClientCxt, c.ID)
    78  		reports = append(reports, &entities.PauseUnpauseReport{Id: c.ID, Err: err})
    79  	}
    80  	return reports, nil
    81  }
    82  
    83  func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, options entities.StopOptions) ([]*entities.StopReport, error) {
    84  	reports := []*entities.StopReport{}
    85  	for _, cidFile := range options.CIDFiles {
    86  		content, err := ioutil.ReadFile(cidFile)
    87  		if err != nil {
    88  			return nil, errors.Wrap(err, "error reading CIDFile")
    89  		}
    90  		id := strings.Split(string(content), "\n")[0]
    91  		namesOrIds = append(namesOrIds, id)
    92  	}
    93  	ctrs, err := getContainersByContext(ic.ClientCxt, options.All, options.Ignore, namesOrIds)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	for _, c := range ctrs {
    98  		report := entities.StopReport{Id: c.ID}
    99  		if err = containers.Stop(ic.ClientCxt, c.ID, options.Timeout); err != nil {
   100  			// These first two are considered non-fatal under the right conditions
   101  			if errors.Cause(err).Error() == define.ErrCtrStopped.Error() {
   102  				logrus.Debugf("Container %s is already stopped", c.ID)
   103  				reports = append(reports, &report)
   104  				continue
   105  			} else if options.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() {
   106  				logrus.Debugf("Container %s is not running, could not stop", c.ID)
   107  				reports = append(reports, &report)
   108  				continue
   109  			}
   110  
   111  			// TODO we need to associate errors returned by http with common
   112  			// define.errors so that we can equity tests. this will allow output
   113  			// to be the same as the native client
   114  			report.Err = err
   115  			reports = append(reports, &report)
   116  			continue
   117  		}
   118  		reports = append(reports, &report)
   119  	}
   120  	return reports, nil
   121  }
   122  
   123  func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, options entities.KillOptions) ([]*entities.KillReport, error) {
   124  	ctrs, err := getContainersByContext(ic.ClientCxt, options.All, false, namesOrIds)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	reports := make([]*entities.KillReport, 0, len(ctrs))
   129  	for _, c := range ctrs {
   130  		reports = append(reports, &entities.KillReport{
   131  			Id:  c.ID,
   132  			Err: containers.Kill(ic.ClientCxt, c.ID, options.Signal),
   133  		})
   134  	}
   135  	return reports, nil
   136  }
   137  
   138  func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, options entities.RestartOptions) ([]*entities.RestartReport, error) {
   139  	var (
   140  		reports = []*entities.RestartReport{}
   141  		timeout *int
   142  	)
   143  	if options.Timeout != nil {
   144  		t := int(*options.Timeout)
   145  		timeout = &t
   146  	}
   147  
   148  	ctrs, err := getContainersByContext(ic.ClientCxt, options.All, false, namesOrIds)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	for _, c := range ctrs {
   153  		if options.Running && c.State != define.ContainerStateRunning.String() {
   154  			continue
   155  		}
   156  		reports = append(reports, &entities.RestartReport{
   157  			Id:  c.ID,
   158  			Err: containers.Restart(ic.ClientCxt, c.ID, timeout),
   159  		})
   160  	}
   161  	return reports, nil
   162  }
   163  
   164  func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*entities.RmReport, error) {
   165  	for _, cidFile := range options.CIDFiles {
   166  		content, err := ioutil.ReadFile(cidFile)
   167  		if err != nil {
   168  			return nil, errors.Wrap(err, "error reading CIDFile")
   169  		}
   170  		id := strings.Split(string(content), "\n")[0]
   171  		namesOrIds = append(namesOrIds, id)
   172  	}
   173  	ctrs, err := getContainersByContext(ic.ClientCxt, options.All, options.Ignore, namesOrIds)
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  	// TODO there is no endpoint for container eviction.  Need to discuss
   178  	reports := make([]*entities.RmReport, 0, len(ctrs))
   179  	for _, c := range ctrs {
   180  		reports = append(reports, &entities.RmReport{
   181  			Id:  c.ID,
   182  			Err: containers.Remove(ic.ClientCxt, c.ID, &options.Force, &options.Volumes),
   183  		})
   184  	}
   185  	return reports, nil
   186  }
   187  
   188  func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) (*entities.ContainerPruneReport, error) {
   189  	return containers.Prune(ic.ClientCxt, options.Filters)
   190  }
   191  
   192  func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, []error, error) {
   193  	var (
   194  		reports = make([]*entities.ContainerInspectReport, 0, len(namesOrIds))
   195  		errs    = []error{}
   196  	)
   197  	for _, name := range namesOrIds {
   198  		inspect, err := containers.Inspect(ic.ClientCxt, name, &options.Size)
   199  		if err != nil {
   200  			errModel, ok := err.(entities.ErrorModel)
   201  			if !ok {
   202  				return nil, nil, err
   203  			}
   204  			if errModel.ResponseCode == 404 {
   205  				errs = append(errs, errors.Errorf("no such container %q", name))
   206  				continue
   207  			}
   208  			return nil, nil, err
   209  		}
   210  		reports = append(reports, &entities.ContainerInspectReport{InspectContainerData: inspect})
   211  	}
   212  	return reports, errs, nil
   213  }
   214  
   215  func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.TopOptions) (*entities.StringSliceReport, error) {
   216  	switch {
   217  	case options.Latest:
   218  		return nil, errors.New("latest is not supported")
   219  	case options.NameOrID == "":
   220  		return nil, errors.New("NameOrID must be specified")
   221  	}
   222  
   223  	topOutput, err := containers.Top(ic.ClientCxt, options.NameOrID, options.Descriptors)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  	return &entities.StringSliceReport{Value: topOutput}, nil
   228  }
   229  
   230  func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, options entities.CommitOptions) (*entities.CommitReport, error) {
   231  	var (
   232  		repo string
   233  		tag  = "latest"
   234  	)
   235  	if len(options.ImageName) > 0 {
   236  		ref, err := reference.Parse(options.ImageName)
   237  		if err != nil {
   238  			return nil, errors.Wrapf(err, "error parsing reference %q", options.ImageName)
   239  		}
   240  		if t, ok := ref.(reference.Tagged); ok {
   241  			tag = t.Tag()
   242  		}
   243  		if r, ok := ref.(reference.Named); ok {
   244  			repo = r.Name()
   245  		}
   246  		if len(repo) < 1 {
   247  			return nil, errors.Errorf("invalid image name %q", options.ImageName)
   248  		}
   249  	}
   250  	commitOpts := containers.CommitOptions{
   251  		Author:  &options.Author,
   252  		Changes: options.Changes,
   253  		Comment: &options.Message,
   254  		Format:  &options.Format,
   255  		Pause:   &options.Pause,
   256  		Repo:    &repo,
   257  		Tag:     &tag,
   258  	}
   259  	response, err := containers.Commit(ic.ClientCxt, nameOrID, commitOpts)
   260  	if err != nil {
   261  		return nil, err
   262  	}
   263  	return &entities.CommitReport{Id: response.ID}, nil
   264  }
   265  
   266  func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string, options entities.ContainerExportOptions) error {
   267  	var (
   268  		err error
   269  		w   io.Writer
   270  	)
   271  	if len(options.Output) > 0 {
   272  		w, err = os.Create(options.Output)
   273  		if err != nil {
   274  			return err
   275  		}
   276  	}
   277  	return containers.Export(ic.ClientCxt, nameOrID, w)
   278  }
   279  
   280  func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options entities.CheckpointOptions) ([]*entities.CheckpointReport, error) {
   281  	var (
   282  		err  error
   283  		ctrs = []entities.ListContainer{}
   284  	)
   285  
   286  	if options.All {
   287  		allCtrs, err := getContainersByContext(ic.ClientCxt, true, false, []string{})
   288  		if err != nil {
   289  			return nil, err
   290  		}
   291  		// narrow the list to running only
   292  		for _, c := range allCtrs {
   293  			if c.State == define.ContainerStateRunning.String() {
   294  				ctrs = append(ctrs, c)
   295  			}
   296  		}
   297  
   298  	} else {
   299  		ctrs, err = getContainersByContext(ic.ClientCxt, false, false, namesOrIds)
   300  		if err != nil {
   301  			return nil, err
   302  		}
   303  	}
   304  	reports := make([]*entities.CheckpointReport, 0, len(ctrs))
   305  	for _, c := range ctrs {
   306  		report, err := containers.Checkpoint(ic.ClientCxt, c.ID, &options.Keep, &options.LeaveRunning, &options.TCPEstablished, &options.IgnoreRootFS, &options.Export)
   307  		if err != nil {
   308  			reports = append(reports, &entities.CheckpointReport{Id: c.ID, Err: err})
   309  		}
   310  		reports = append(reports, report)
   311  	}
   312  	return reports, nil
   313  }
   314  
   315  func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, options entities.RestoreOptions) ([]*entities.RestoreReport, error) {
   316  	var (
   317  		err  error
   318  		ctrs = []entities.ListContainer{}
   319  	)
   320  	if options.All {
   321  		allCtrs, err := getContainersByContext(ic.ClientCxt, true, false, []string{})
   322  		if err != nil {
   323  			return nil, err
   324  		}
   325  		// narrow the list to exited only
   326  		for _, c := range allCtrs {
   327  			if c.State == define.ContainerStateExited.String() {
   328  				ctrs = append(ctrs, c)
   329  			}
   330  		}
   331  
   332  	} else {
   333  		ctrs, err = getContainersByContext(ic.ClientCxt, false, false, namesOrIds)
   334  		if err != nil {
   335  			return nil, err
   336  		}
   337  	}
   338  	reports := make([]*entities.RestoreReport, 0, len(ctrs))
   339  	for _, c := range ctrs {
   340  		report, err := containers.Restore(ic.ClientCxt, c.ID, &options.Keep, &options.TCPEstablished, &options.IgnoreRootFS, &options.IgnoreStaticIP, &options.IgnoreStaticMAC, &options.Name, &options.Import)
   341  		if err != nil {
   342  			reports = append(reports, &entities.RestoreReport{Id: c.ID, Err: err})
   343  		}
   344  		reports = append(reports, report)
   345  	}
   346  	return reports, nil
   347  }
   348  
   349  func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*entities.ContainerCreateReport, error) {
   350  	response, err := containers.CreateWithSpec(ic.ClientCxt, s)
   351  	if err != nil {
   352  		return nil, err
   353  	}
   354  	for _, w := range response.Warnings {
   355  		fmt.Fprintf(os.Stderr, "%s\n", w)
   356  	}
   357  	return &entities.ContainerCreateReport{Id: response.ID}, nil
   358  }
   359  
   360  func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, options entities.ContainerLogsOptions) error {
   361  	since := options.Since.Format(time.RFC3339)
   362  	tail := strconv.FormatInt(options.Tail, 10)
   363  	stdout := options.Writer != nil
   364  	opts := containers.LogOptions{
   365  		Follow:     &options.Follow,
   366  		Since:      &since,
   367  		Stderr:     &stdout,
   368  		Stdout:     &stdout,
   369  		Tail:       &tail,
   370  		Timestamps: &options.Timestamps,
   371  		Until:      nil,
   372  	}
   373  
   374  	var err error
   375  	outCh := make(chan string)
   376  	ctx, cancel := context.WithCancel(context.Background())
   377  	go func() {
   378  		err = containers.Logs(ic.ClientCxt, nameOrIDs[0], opts, outCh, outCh)
   379  		cancel()
   380  	}()
   381  
   382  	for {
   383  		select {
   384  		case <-ctx.Done():
   385  			return err
   386  		case line := <-outCh:
   387  			_, _ = io.WriteString(options.Writer, line+"\n")
   388  		}
   389  	}
   390  }
   391  
   392  func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, options entities.AttachOptions) error {
   393  	ctrs, err := getContainersByContext(ic.ClientCxt, false, false, []string{nameOrID})
   394  	if err != nil {
   395  		return err
   396  	}
   397  	ctr := ctrs[0]
   398  	if ctr.State != define.ContainerStateRunning.String() {
   399  		return errors.Errorf("you can only attach to running containers")
   400  	}
   401  
   402  	return containers.Attach(ic.ClientCxt, nameOrID, &options.DetachKeys, nil, bindings.PTrue, options.Stdin, options.Stdout, options.Stderr, nil)
   403  }
   404  
   405  func makeExecConfig(options entities.ExecOptions) *handlers.ExecCreateConfig {
   406  	env := []string{}
   407  	for k, v := range options.Envs {
   408  		env = append(env, fmt.Sprintf("%s=%s", k, v))
   409  	}
   410  
   411  	createConfig := new(handlers.ExecCreateConfig)
   412  	createConfig.User = options.User
   413  	createConfig.Privileged = options.Privileged
   414  	createConfig.Tty = options.Tty
   415  	createConfig.AttachStdin = options.Interactive
   416  	createConfig.AttachStdout = true
   417  	createConfig.AttachStderr = true
   418  	createConfig.Detach = false
   419  	createConfig.DetachKeys = options.DetachKeys
   420  	createConfig.Env = env
   421  	createConfig.WorkingDir = options.WorkDir
   422  	createConfig.Cmd = options.Cmd
   423  
   424  	return createConfig
   425  }
   426  
   427  func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrID string, options entities.ExecOptions, streams define.AttachStreams) (int, error) {
   428  	createConfig := makeExecConfig(options)
   429  
   430  	sessionID, err := containers.ExecCreate(ic.ClientCxt, nameOrID, createConfig)
   431  	if err != nil {
   432  		return 125, err
   433  	}
   434  
   435  	if err := containers.ExecStartAndAttach(ic.ClientCxt, sessionID, &streams); err != nil {
   436  		return 125, err
   437  	}
   438  
   439  	inspectOut, err := containers.ExecInspect(ic.ClientCxt, sessionID)
   440  	if err != nil {
   441  		return 125, err
   442  	}
   443  
   444  	return inspectOut.ExitCode, nil
   445  }
   446  
   447  func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID string, options entities.ExecOptions) (string, error) {
   448  	createConfig := makeExecConfig(options)
   449  
   450  	sessionID, err := containers.ExecCreate(ic.ClientCxt, nameOrID, createConfig)
   451  	if err != nil {
   452  		return "", err
   453  	}
   454  
   455  	if err := containers.ExecStart(ic.ClientCxt, sessionID); err != nil {
   456  		return "", err
   457  	}
   458  
   459  	return sessionID, nil
   460  }
   461  
   462  func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, output, errput *os.File) error { //nolint
   463  	attachErr := make(chan error)
   464  	attachReady := make(chan bool)
   465  	go func() {
   466  		err := containers.Attach(ic.ClientCxt, name, detachKeys, bindings.PFalse, bindings.PTrue, input, output, errput, attachReady)
   467  		attachErr <- err
   468  	}()
   469  	// Wait for the attach to actually happen before starting
   470  	// the container.
   471  	select {
   472  	case <-attachReady:
   473  		if err := containers.Start(ic.ClientCxt, name, detachKeys); err != nil {
   474  			return err
   475  		}
   476  	case err := <-attachErr:
   477  		return err
   478  	}
   479  	// If attachReady happens first, wait for containers.Attach to complete
   480  	return <-attachErr
   481  }
   482  
   483  func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) {
   484  	reports := []*entities.ContainerStartReport{}
   485  	var exitCode = define.ExecErrorCodeGeneric
   486  	ctrs, err := getContainersByContext(ic.ClientCxt, false, false, namesOrIds)
   487  	if err != nil {
   488  		return nil, err
   489  	}
   490  	// There can only be one container if attach was used
   491  	for i, ctr := range ctrs {
   492  		name := ctr.ID
   493  		report := entities.ContainerStartReport{
   494  			Id:       name,
   495  			RawInput: namesOrIds[i],
   496  			ExitCode: exitCode,
   497  		}
   498  		ctrRunning := ctr.State == define.ContainerStateRunning.String()
   499  		if options.Attach {
   500  			err = startAndAttach(ic, name, &options.DetachKeys, options.Stdin, options.Stdout, options.Stderr)
   501  			if err == define.ErrDetach {
   502  				// User manually detached
   503  				// Exit cleanly immediately
   504  				reports = append(reports, &report)
   505  				return reports, nil
   506  			}
   507  			if ctrRunning {
   508  				reports = append(reports, &report)
   509  				return reports, nil
   510  			}
   511  
   512  			if err != nil {
   513  				report.ExitCode = define.ExitCode(report.Err)
   514  				report.Err = err
   515  				reports = append(reports, &report)
   516  				return reports, errors.Wrapf(report.Err, "unable to start container %s", name)
   517  			}
   518  			exitCode, err := containers.Wait(ic.ClientCxt, name, nil)
   519  			if err == define.ErrNoSuchCtr {
   520  				// Check events
   521  				event, err := ic.GetLastContainerEvent(ctx, name, events.Exited)
   522  				if err != nil {
   523  					logrus.Errorf("Cannot get exit code: %v", err)
   524  					report.ExitCode = define.ExecErrorCodeNotFound
   525  				} else {
   526  					report.ExitCode = event.ContainerExitCode
   527  				}
   528  			} else {
   529  				report.ExitCode = int(exitCode)
   530  			}
   531  			reports = append(reports, &report)
   532  			return reports, nil
   533  		}
   534  		// Start the container if it's not running already.
   535  		if !ctrRunning {
   536  			err = containers.Start(ic.ClientCxt, name, &options.DetachKeys)
   537  			if err != nil {
   538  				report.Err = errors.Wrapf(err, "unable to start container %q", name)
   539  				report.ExitCode = define.ExitCode(err)
   540  				reports = append(reports, &report)
   541  				continue
   542  			}
   543  		}
   544  		report.ExitCode = 0
   545  		reports = append(reports, &report)
   546  	}
   547  	return reports, nil
   548  }
   549  
   550  func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.ContainerListOptions) ([]entities.ListContainer, error) {
   551  	return containers.List(ic.ClientCxt, options.Filters, &options.All, &options.Last, &options.Namespace, &options.Size, &options.Sync)
   552  }
   553  
   554  func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) {
   555  	con, err := containers.CreateWithSpec(ic.ClientCxt, opts.Spec)
   556  	if err != nil {
   557  		return nil, err
   558  	}
   559  	for _, w := range con.Warnings {
   560  		fmt.Fprintf(os.Stderr, "%s\n", w)
   561  	}
   562  	if opts.CIDFile != "" {
   563  		if err := util.CreateCidFile(opts.CIDFile, con.ID); err != nil {
   564  			return nil, err
   565  		}
   566  	}
   567  
   568  	report := entities.ContainerRunReport{Id: con.ID}
   569  
   570  	if opts.Detach {
   571  		// Detach and return early
   572  		err := containers.Start(ic.ClientCxt, con.ID, nil)
   573  		if err != nil {
   574  			report.ExitCode = define.ExitCode(err)
   575  		}
   576  		return &report, err
   577  	}
   578  
   579  	// Attach
   580  	if err := startAndAttach(ic, con.ID, &opts.DetachKeys, opts.InputStream, opts.OutputStream, opts.ErrorStream); err != nil {
   581  		if err == define.ErrDetach {
   582  			return &report, nil
   583  		}
   584  
   585  		report.ExitCode = define.ExitCode(err)
   586  		if opts.Rm {
   587  			if rmErr := containers.Remove(ic.ClientCxt, con.ID, bindings.PFalse, bindings.PTrue); rmErr != nil {
   588  				logrus.Debugf("unable to remove container %s after failing to start and attach to it", con.ID)
   589  			}
   590  		}
   591  		return &report, err
   592  	}
   593  
   594  	if opts.Rm {
   595  		// Defer the removal, so we can return early if needed and
   596  		// de-spaghetti the code.
   597  		defer func() {
   598  			shouldRestart, err := containers.ShouldRestart(ic.ClientCxt, con.ID)
   599  			if err != nil {
   600  				logrus.Errorf("Failed to check if %s should restart: %v", con.ID, err)
   601  				return
   602  			}
   603  
   604  			if !shouldRestart {
   605  				if err := containers.Remove(ic.ClientCxt, con.ID, bindings.PFalse, bindings.PTrue); err != nil {
   606  					if errorhandling.Contains(err, define.ErrNoSuchCtr) ||
   607  						errorhandling.Contains(err, define.ErrCtrRemoved) {
   608  						logrus.Warnf("Container %s does not exist: %v", con.ID, err)
   609  					} else {
   610  						logrus.Errorf("Error removing container %s: %v", con.ID, err)
   611  					}
   612  				}
   613  			}
   614  		}()
   615  	}
   616  
   617  	// Wait
   618  	exitCode, waitErr := containers.Wait(ic.ClientCxt, con.ID, nil)
   619  	if waitErr == nil {
   620  		report.ExitCode = int(exitCode)
   621  		return &report, nil
   622  	}
   623  
   624  	// Determine why the wait failed.  If the container doesn't exist,
   625  	// consult the events.
   626  	if !errorhandling.Contains(waitErr, define.ErrNoSuchCtr) {
   627  		return &report, waitErr
   628  	}
   629  
   630  	// Events
   631  	eventsChannel := make(chan *events.Event)
   632  	eventOptions := entities.EventsOptions{
   633  		EventChan: eventsChannel,
   634  		Filter: []string{
   635  			"type=container",
   636  			fmt.Sprintf("container=%s", con.ID),
   637  			fmt.Sprintf("event=%s", events.Exited),
   638  		},
   639  	}
   640  
   641  	var lastEvent *events.Event
   642  	var mutex sync.Mutex
   643  	mutex.Lock()
   644  	// Read the events.
   645  	go func() {
   646  		for e := range eventsChannel {
   647  			lastEvent = e
   648  		}
   649  		mutex.Unlock()
   650  	}()
   651  
   652  	eventsErr := ic.Events(ctx, eventOptions)
   653  
   654  	// Wait for all events to be read
   655  	mutex.Lock()
   656  	if eventsErr != nil || lastEvent == nil {
   657  		logrus.Errorf("Cannot get exit code: %v", err)
   658  		report.ExitCode = define.ExecErrorCodeNotFound
   659  		return &report, nil // compat with local client
   660  	}
   661  
   662  	report.ExitCode = lastEvent.ContainerExitCode
   663  	return &report, err
   664  }
   665  
   666  func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) {
   667  	changes, err := containers.Diff(ic.ClientCxt, nameOrID)
   668  	return &entities.DiffReport{Changes: changes}, err
   669  }
   670  
   671  func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []string, options entities.ContainerCleanupOptions) ([]*entities.ContainerCleanupReport, error) {
   672  	return nil, errors.New("not implemented")
   673  }
   674  
   675  func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []string, options entities.ContainerInitOptions) ([]*entities.ContainerInitReport, error) {
   676  	ctrs, err := getContainersByContext(ic.ClientCxt, options.All, false, namesOrIds)
   677  	if err != nil {
   678  		return nil, err
   679  	}
   680  	reports := make([]*entities.ContainerInitReport, 0, len(ctrs))
   681  	for _, ctr := range ctrs {
   682  		err := containers.ContainerInit(ic.ClientCxt, ctr.ID)
   683  		// When using all, it is NOT considered an error if a container
   684  		// has already been init'd.
   685  		if err != nil && options.All && strings.Contains(errors.Cause(err).Error(), define.ErrCtrStateInvalid.Error()) {
   686  			err = nil
   687  		}
   688  		reports = append(reports, &entities.ContainerInitReport{
   689  			Err: err,
   690  			Id:  ctr.ID,
   691  		})
   692  	}
   693  	return reports, nil
   694  }
   695  
   696  func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []string, options entities.ContainerMountOptions) ([]*entities.ContainerMountReport, error) {
   697  	return nil, errors.New("mounting containers is not supported for remote clients")
   698  }
   699  
   700  func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIDs []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) {
   701  	return nil, errors.New("unmounting containers is not supported for remote clients")
   702  }
   703  
   704  func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) {
   705  	return config.Default()
   706  }
   707  
   708  func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) {
   709  	var (
   710  		reports    = []*entities.ContainerPortReport{}
   711  		namesOrIds = []string{}
   712  	)
   713  	if len(nameOrID) > 0 {
   714  		namesOrIds = append(namesOrIds, nameOrID)
   715  	}
   716  	ctrs, err := getContainersByContext(ic.ClientCxt, options.All, false, namesOrIds)
   717  	if err != nil {
   718  		return nil, err
   719  	}
   720  	for _, con := range ctrs {
   721  		if con.State != define.ContainerStateRunning.String() {
   722  			continue
   723  		}
   724  		if len(con.Ports) > 0 {
   725  			reports = append(reports, &entities.ContainerPortReport{
   726  				Id:    con.ID,
   727  				Ports: con.Ports,
   728  			})
   729  		}
   730  	}
   731  	return reports, nil
   732  }
   733  
   734  func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) (*entities.ContainerCpReport, error) {
   735  	return nil, errors.New("not implemented")
   736  }
   737  
   738  // Shutdown Libpod engine
   739  func (ic *ContainerEngine) Shutdown(_ context.Context) {
   740  }
   741  
   742  func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []string, options entities.ContainerStatsOptions) (statsChan chan entities.ContainerStatsReport, err error) {
   743  	if options.Latest {
   744  		return nil, errors.New("latest is not supported for the remote client")
   745  	}
   746  	return containers.Stats(ic.ClientCxt, namesOrIds, &options.Stream)
   747  }
   748  
   749  // ShouldRestart reports back whether the containre will restart
   750  func (ic *ContainerEngine) ShouldRestart(_ context.Context, id string) (bool, error) {
   751  	return containers.ShouldRestart(ic.ClientCxt, id)
   752  }