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