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 }