8758d08bb4
API v1.20 (Docker Engine v1.11.0) and older allowed a HostConfig to be passed when starting a container. This feature was deprecated in API v1.21 (Docker Engine v1.10.0) in3e7405aea8
, and removed in API v1.23 (Docker Engine v1.12.0) in commit0a8386c8be
. API v1.23 and older are deprecated, and this patch removes the feature. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
138 lines
3.9 KiB
Go
138 lines
3.9 KiB
Go
package dockerfile // import "github.com/docker/docker/builder/dockerfile"
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/containerd/log"
|
|
"github.com/docker/docker/api/types/backend"
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/builder"
|
|
containerpkg "github.com/docker/docker/container"
|
|
"github.com/docker/docker/errdefs"
|
|
"github.com/docker/docker/pkg/stringid"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type containerManager struct {
|
|
tmpContainers map[string]struct{}
|
|
backend builder.ExecBackend
|
|
}
|
|
|
|
// newContainerManager creates a new container backend
|
|
func newContainerManager(docker builder.ExecBackend) *containerManager {
|
|
return &containerManager{
|
|
backend: docker,
|
|
tmpContainers: make(map[string]struct{}),
|
|
}
|
|
}
|
|
|
|
// Create a container
|
|
func (c *containerManager) Create(ctx context.Context, runConfig *container.Config, hostConfig *container.HostConfig) (container.CreateResponse, error) {
|
|
ctr, err := c.backend.ContainerCreateIgnoreImagesArgsEscaped(ctx, backend.ContainerCreateConfig{
|
|
Config: runConfig,
|
|
HostConfig: hostConfig,
|
|
})
|
|
if err != nil {
|
|
return ctr, err
|
|
}
|
|
c.tmpContainers[ctr.ID] = struct{}{}
|
|
return ctr, nil
|
|
}
|
|
|
|
var errCancelled = errors.New("build cancelled")
|
|
|
|
// Run a container by ID
|
|
func (c *containerManager) Run(ctx context.Context, cID string, stdout, stderr io.Writer) (err error) {
|
|
attached := make(chan struct{})
|
|
errCh := make(chan error, 1)
|
|
go func() {
|
|
errCh <- c.backend.ContainerAttachRaw(cID, nil, stdout, stderr, true, attached)
|
|
}()
|
|
select {
|
|
case err := <-errCh:
|
|
return err
|
|
case <-attached:
|
|
}
|
|
|
|
finished := make(chan struct{})
|
|
cancelErrCh := make(chan error, 1)
|
|
go func() {
|
|
select {
|
|
case <-ctx.Done():
|
|
log.G(ctx).Debugln("Build cancelled, removing container:", cID)
|
|
err = c.backend.ContainerRm(cID, &backend.ContainerRmConfig{ForceRemove: true, RemoveVolume: true})
|
|
if err != nil {
|
|
_, _ = fmt.Fprintf(stdout, "Removing container %s: %v\n", stringid.TruncateID(cID), err)
|
|
}
|
|
cancelErrCh <- errCancelled
|
|
case <-finished:
|
|
cancelErrCh <- nil
|
|
}
|
|
}()
|
|
|
|
if err := c.backend.ContainerStart(ctx, cID, "", ""); err != nil {
|
|
close(finished)
|
|
logCancellationError(cancelErrCh, "error from ContainerStart: "+err.Error())
|
|
return err
|
|
}
|
|
|
|
// Block on reading output from container, stop on err or chan closed
|
|
if err := <-errCh; err != nil {
|
|
close(finished)
|
|
logCancellationError(cancelErrCh, "error from errCh: "+err.Error())
|
|
return err
|
|
}
|
|
|
|
waitC, err := c.backend.ContainerWait(ctx, cID, containerpkg.WaitConditionNotRunning)
|
|
if err != nil {
|
|
close(finished)
|
|
logCancellationError(cancelErrCh, fmt.Sprintf("unable to begin ContainerWait: %s", err))
|
|
return err
|
|
}
|
|
|
|
if status := <-waitC; status.ExitCode() != 0 {
|
|
close(finished)
|
|
logCancellationError(cancelErrCh,
|
|
fmt.Sprintf("a non-zero code from ContainerWait: %d", status.ExitCode()))
|
|
return &statusCodeError{code: status.ExitCode(), err: status.Err()}
|
|
}
|
|
|
|
close(finished)
|
|
return <-cancelErrCh
|
|
}
|
|
|
|
func logCancellationError(cancelErrCh chan error, msg string) {
|
|
if cancelErr := <-cancelErrCh; cancelErr != nil {
|
|
log.G(context.TODO()).Debugf("Build cancelled (%v): %s", cancelErr, msg)
|
|
}
|
|
}
|
|
|
|
type statusCodeError struct {
|
|
code int
|
|
err error
|
|
}
|
|
|
|
func (e *statusCodeError) Error() string {
|
|
if e.err == nil {
|
|
return ""
|
|
}
|
|
return e.err.Error()
|
|
}
|
|
|
|
func (e *statusCodeError) StatusCode() int {
|
|
return e.code
|
|
}
|
|
|
|
// RemoveAll containers managed by this container manager
|
|
func (c *containerManager) RemoveAll(stdout io.Writer) {
|
|
for containerID := range c.tmpContainers {
|
|
if err := c.backend.ContainerRm(containerID, &backend.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil && !errdefs.IsNotFound(err) {
|
|
_, _ = fmt.Fprintf(stdout, "Removing intermediate container %s: %v\n", stringid.TruncateID(containerID), err)
|
|
continue
|
|
}
|
|
delete(c.tmpContainers, containerID)
|
|
_, _ = fmt.Fprintf(stdout, " ---> Removed intermediate container %s\n", stringid.TruncateID(containerID))
|
|
}
|
|
}
|