containerbackend.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. package dockerfile // import "github.com/docker/docker/builder/dockerfile"
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "github.com/containerd/log"
  7. "github.com/docker/docker/api/types/backend"
  8. "github.com/docker/docker/api/types/container"
  9. "github.com/docker/docker/builder"
  10. containerpkg "github.com/docker/docker/container"
  11. "github.com/docker/docker/errdefs"
  12. "github.com/docker/docker/pkg/stringid"
  13. "github.com/pkg/errors"
  14. )
  15. type containerManager struct {
  16. tmpContainers map[string]struct{}
  17. backend builder.ExecBackend
  18. }
  19. // newContainerManager creates a new container backend
  20. func newContainerManager(docker builder.ExecBackend) *containerManager {
  21. return &containerManager{
  22. backend: docker,
  23. tmpContainers: make(map[string]struct{}),
  24. }
  25. }
  26. // Create a container
  27. func (c *containerManager) Create(ctx context.Context, runConfig *container.Config, hostConfig *container.HostConfig) (container.CreateResponse, error) {
  28. ctr, err := c.backend.ContainerCreateIgnoreImagesArgsEscaped(ctx, backend.ContainerCreateConfig{
  29. Config: runConfig,
  30. HostConfig: hostConfig,
  31. })
  32. if err != nil {
  33. return ctr, err
  34. }
  35. c.tmpContainers[ctr.ID] = struct{}{}
  36. return ctr, nil
  37. }
  38. var errCancelled = errors.New("build cancelled")
  39. // Run a container by ID
  40. func (c *containerManager) Run(ctx context.Context, cID string, stdout, stderr io.Writer) (err error) {
  41. attached := make(chan struct{})
  42. errCh := make(chan error, 1)
  43. go func() {
  44. errCh <- c.backend.ContainerAttachRaw(cID, nil, stdout, stderr, true, attached)
  45. }()
  46. select {
  47. case err := <-errCh:
  48. return err
  49. case <-attached:
  50. }
  51. finished := make(chan struct{})
  52. cancelErrCh := make(chan error, 1)
  53. go func() {
  54. select {
  55. case <-ctx.Done():
  56. log.G(ctx).Debugln("Build cancelled, killing and removing container:", cID)
  57. c.backend.ContainerKill(cID, "")
  58. err = c.backend.ContainerRm(cID, &backend.ContainerRmConfig{ForceRemove: true, RemoveVolume: true})
  59. if err != nil {
  60. _, _ = fmt.Fprintf(stdout, "Removing container %s: %v\n", stringid.TruncateID(cID), err)
  61. }
  62. cancelErrCh <- errCancelled
  63. case <-finished:
  64. cancelErrCh <- nil
  65. }
  66. }()
  67. if err := c.backend.ContainerStart(ctx, cID, nil, "", ""); err != nil {
  68. close(finished)
  69. logCancellationError(cancelErrCh, "error from ContainerStart: "+err.Error())
  70. return err
  71. }
  72. // Block on reading output from container, stop on err or chan closed
  73. if err := <-errCh; err != nil {
  74. close(finished)
  75. logCancellationError(cancelErrCh, "error from errCh: "+err.Error())
  76. return err
  77. }
  78. waitC, err := c.backend.ContainerWait(ctx, cID, containerpkg.WaitConditionNotRunning)
  79. if err != nil {
  80. close(finished)
  81. logCancellationError(cancelErrCh, fmt.Sprintf("unable to begin ContainerWait: %s", err))
  82. return err
  83. }
  84. if status := <-waitC; status.ExitCode() != 0 {
  85. close(finished)
  86. logCancellationError(cancelErrCh,
  87. fmt.Sprintf("a non-zero code from ContainerWait: %d", status.ExitCode()))
  88. return &statusCodeError{code: status.ExitCode(), err: status.Err()}
  89. }
  90. close(finished)
  91. return <-cancelErrCh
  92. }
  93. func logCancellationError(cancelErrCh chan error, msg string) {
  94. if cancelErr := <-cancelErrCh; cancelErr != nil {
  95. log.G(context.TODO()).Debugf("Build cancelled (%v): %s", cancelErr, msg)
  96. }
  97. }
  98. type statusCodeError struct {
  99. code int
  100. err error
  101. }
  102. func (e *statusCodeError) Error() string {
  103. if e.err == nil {
  104. return ""
  105. }
  106. return e.err.Error()
  107. }
  108. func (e *statusCodeError) StatusCode() int {
  109. return e.code
  110. }
  111. // RemoveAll containers managed by this container manager
  112. func (c *containerManager) RemoveAll(stdout io.Writer) {
  113. for containerID := range c.tmpContainers {
  114. if err := c.backend.ContainerRm(containerID, &backend.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil && !errdefs.IsNotFound(err) {
  115. _, _ = fmt.Fprintf(stdout, "Removing intermediate container %s: %v\n", stringid.TruncateID(containerID), err)
  116. continue
  117. }
  118. delete(c.tmpContainers, containerID)
  119. _, _ = fmt.Fprintf(stdout, " ---> Removed intermediate container %s\n", stringid.TruncateID(containerID))
  120. }
  121. }