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  }