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