containerbackend.go 3.9 KB

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