github.com/lazyboychen7/engine@v17.12.1-ce-rc2+incompatible/builder/dockerfile/containerbackend.go (about)

     1  package dockerfile
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  
     7  	"github.com/docker/docker/api/types"
     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/pkg/stringid"
    12  	"github.com/pkg/errors"
    13  	"github.com/sirupsen/logrus"
    14  	"golang.org/x/net/context"
    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, platform string) (container.ContainerCreateCreatedBody, error) {
    32  	container, err := c.backend.ContainerCreate(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: 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  	return e.err.Error()
   116  }
   117  
   118  func (e *statusCodeError) StatusCode() int {
   119  	return e.code
   120  }
   121  
   122  func (c *containerManager) removeContainer(containerID string, stdout io.Writer) error {
   123  	rmConfig := &types.ContainerRmConfig{
   124  		ForceRemove:  true,
   125  		RemoveVolume: true,
   126  	}
   127  	if err := c.backend.ContainerRm(containerID, rmConfig); err != nil {
   128  		fmt.Fprintf(stdout, "Error removing intermediate container %s: %v\n", stringid.TruncateID(containerID), err)
   129  		return err
   130  	}
   131  	return nil
   132  }
   133  
   134  // RemoveAll containers managed by this container manager
   135  func (c *containerManager) RemoveAll(stdout io.Writer) {
   136  	for containerID := range c.tmpContainers {
   137  		if err := c.removeContainer(containerID, stdout); err != nil {
   138  			return
   139  		}
   140  		delete(c.tmpContainers, containerID)
   141  		fmt.Fprintf(stdout, "Removing intermediate container %s\n", stringid.TruncateID(containerID))
   142  	}
   143  }