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  }