mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
14215ed5a1
Currently builder.Backend is implemented by daemonbuilder.Docker{} for the daemon. This registration happens in the API/server code. However, this is too implementation specific. Ideally we should be able to specify that docker daemon (or any other) is implementing the Backend and abstract the implementation details. So we should remove package daemonbuilder dependency in build_routes.go With this change, daemonbuilder.Docker is nothing more than the daemon. A follow on change will remove the daemonbuilder package and move relevant methods under daemon, so that API only knows about the backend. Also cleanup code in api/client/build.go. docker cli always performs build context tar download for remoteURLs and sends an empty remoteContext. So remove relevant dead code. Signed-off-by: Anusha Ragunathan <anusha@docker.com>
235 lines
7.3 KiB
Go
235 lines
7.3 KiB
Go
package daemonbuilder
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/docker/docker/api"
|
|
"github.com/docker/docker/builder"
|
|
"github.com/docker/docker/daemon"
|
|
"github.com/docker/docker/image"
|
|
"github.com/docker/docker/pkg/archive"
|
|
"github.com/docker/docker/pkg/chrootarchive"
|
|
"github.com/docker/docker/pkg/httputils"
|
|
"github.com/docker/docker/pkg/idtools"
|
|
"github.com/docker/docker/pkg/ioutils"
|
|
"github.com/docker/docker/pkg/urlutil"
|
|
"github.com/docker/docker/reference"
|
|
"github.com/docker/docker/registry"
|
|
"github.com/docker/engine-api/types"
|
|
"github.com/docker/engine-api/types/container"
|
|
)
|
|
|
|
// Docker implements builder.Backend for the docker Daemon object.
|
|
type Docker struct {
|
|
*daemon.Daemon
|
|
}
|
|
|
|
// ensure Docker implements builder.Backend
|
|
var _ builder.Backend = Docker{}
|
|
|
|
// Pull tells Docker to pull image referenced by `name`.
|
|
func (d Docker) Pull(name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
|
|
ref, err := reference.ParseNamed(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ref = reference.WithDefaultTag(ref)
|
|
|
|
pullRegistryAuth := &types.AuthConfig{}
|
|
if len(authConfigs) > 0 {
|
|
// The request came with a full auth config file, we prefer to use that
|
|
repoInfo, err := d.Daemon.RegistryService.ResolveRepository(ref)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resolvedConfig := registry.ResolveAuthConfig(
|
|
authConfigs,
|
|
repoInfo.Index,
|
|
)
|
|
pullRegistryAuth = &resolvedConfig
|
|
}
|
|
|
|
if err := d.Daemon.PullImage(ref, nil, pullRegistryAuth, ioutils.NopWriteCloser(output)); err != nil {
|
|
return nil, err
|
|
}
|
|
return d.GetImage(name)
|
|
}
|
|
|
|
// GetImage looks up a Docker image referenced by `name`.
|
|
func (d Docker) GetImage(name string) (builder.Image, error) {
|
|
img, err := d.Daemon.GetImage(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return imgWrap{img}, nil
|
|
}
|
|
|
|
// ContainerUpdateCmd updates Path and Args for the container with ID cID.
|
|
func (d Docker) ContainerUpdateCmd(cID string, cmd []string) error {
|
|
c, err := d.Daemon.GetContainer(cID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.Path = cmd[0]
|
|
c.Args = cmd[1:]
|
|
return nil
|
|
}
|
|
|
|
// ContainerAttach attaches streams to the container cID. If stream is true, it streams the output.
|
|
func (d Docker) ContainerAttach(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error {
|
|
return d.Daemon.ContainerWsAttachWithLogs(cID, &daemon.ContainerWsAttachWithLogsConfig{
|
|
InStream: stdin,
|
|
OutStream: stdout,
|
|
ErrStream: stderr,
|
|
Stream: stream,
|
|
})
|
|
}
|
|
|
|
// BuilderCopy copies/extracts a source FileInfo to a destination path inside a container
|
|
// specified by a container object.
|
|
// TODO: make sure callers don't unnecessarily convert destPath with filepath.FromSlash (Copy does it already).
|
|
// BuilderCopy should take in abstract paths (with slashes) and the implementation should convert it to OS-specific paths.
|
|
func (d Docker) BuilderCopy(cID string, destPath string, src builder.FileInfo, decompress bool) error {
|
|
srcPath := src.Path()
|
|
destExists := true
|
|
destDir := false
|
|
rootUID, rootGID := d.Daemon.GetRemappedUIDGID()
|
|
|
|
// Work in daemon-local OS specific file paths
|
|
destPath = filepath.FromSlash(destPath)
|
|
|
|
c, err := d.Daemon.GetContainer(cID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = d.Daemon.Mount(c)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer d.Daemon.Unmount(c)
|
|
|
|
dest, err := c.GetResourcePath(destPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Preserve the trailing slash
|
|
// TODO: why are we appending another path separator if there was already one?
|
|
if strings.HasSuffix(destPath, string(os.PathSeparator)) || destPath == "." {
|
|
destDir = true
|
|
dest += string(os.PathSeparator)
|
|
}
|
|
|
|
destPath = dest
|
|
|
|
destStat, err := os.Stat(destPath)
|
|
if err != nil {
|
|
if !os.IsNotExist(err) {
|
|
logrus.Errorf("Error performing os.Stat on %s. %s", destPath, err)
|
|
return err
|
|
}
|
|
destExists = false
|
|
}
|
|
|
|
uidMaps, gidMaps := d.Daemon.GetUIDGIDMaps()
|
|
archiver := &archive.Archiver{
|
|
Untar: chrootarchive.Untar,
|
|
UIDMaps: uidMaps,
|
|
GIDMaps: gidMaps,
|
|
}
|
|
|
|
if src.IsDir() {
|
|
// copy as directory
|
|
if err := archiver.CopyWithTar(srcPath, destPath); err != nil {
|
|
return err
|
|
}
|
|
return fixPermissions(srcPath, destPath, rootUID, rootGID, destExists)
|
|
}
|
|
if decompress && archive.IsArchivePath(srcPath) {
|
|
// Only try to untar if it is a file and that we've been told to decompress (when ADD-ing a remote file)
|
|
|
|
// First try to unpack the source as an archive
|
|
// to support the untar feature we need to clean up the path a little bit
|
|
// because tar is very forgiving. First we need to strip off the archive's
|
|
// filename from the path but this is only added if it does not end in slash
|
|
tarDest := destPath
|
|
if strings.HasSuffix(tarDest, string(os.PathSeparator)) {
|
|
tarDest = filepath.Dir(destPath)
|
|
}
|
|
|
|
// try to successfully untar the orig
|
|
err := archiver.UntarPath(srcPath, tarDest)
|
|
if err != nil {
|
|
logrus.Errorf("Couldn't untar to %s: %v", tarDest, err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// only needed for fixPermissions, but might as well put it before CopyFileWithTar
|
|
if destDir || (destExists && destStat.IsDir()) {
|
|
destPath = filepath.Join(destPath, src.Name())
|
|
}
|
|
|
|
if err := idtools.MkdirAllNewAs(filepath.Dir(destPath), 0755, rootUID, rootGID); err != nil {
|
|
return err
|
|
}
|
|
if err := archiver.CopyFileWithTar(srcPath, destPath); err != nil {
|
|
return err
|
|
}
|
|
|
|
return fixPermissions(srcPath, destPath, rootUID, rootGID, destExists)
|
|
}
|
|
|
|
// GetCachedImage returns a reference to a cached image whose parent equals `parent`
|
|
// and runconfig equals `cfg`. A cache miss is expected to return an empty ID and a nil error.
|
|
func (d Docker) GetCachedImage(imgID string, cfg *container.Config) (string, error) {
|
|
cache, err := d.Daemon.ImageGetCached(image.ID(imgID), cfg)
|
|
if cache == nil || err != nil {
|
|
return "", err
|
|
}
|
|
return cache.ID().String(), nil
|
|
}
|
|
|
|
// Following is specific to builder contexts
|
|
|
|
// DetectContextFromRemoteURL returns a context and in certain cases the name of the dockerfile to be used
|
|
// irrespective of user input.
|
|
// progressReader is only used if remoteURL is actually a URL (not empty, and not a Git endpoint).
|
|
func DetectContextFromRemoteURL(r io.ReadCloser, remoteURL string, createProgressReader func(in io.ReadCloser) io.ReadCloser) (context builder.ModifiableContext, dockerfileName string, err error) {
|
|
switch {
|
|
case remoteURL == "":
|
|
context, err = builder.MakeTarSumContext(r)
|
|
case urlutil.IsGitURL(remoteURL):
|
|
context, err = builder.MakeGitContext(remoteURL)
|
|
case urlutil.IsURL(remoteURL):
|
|
context, err = builder.MakeRemoteContext(remoteURL, map[string]func(io.ReadCloser) (io.ReadCloser, error){
|
|
httputils.MimeTypes.TextPlain: func(rc io.ReadCloser) (io.ReadCloser, error) {
|
|
dockerfile, err := ioutil.ReadAll(rc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// dockerfileName is set to signal that the remote was interpreted as a single Dockerfile, in which case the caller
|
|
// should use dockerfileName as the new name for the Dockerfile, irrespective of any other user input.
|
|
dockerfileName = api.DefaultDockerfileName
|
|
|
|
// TODO: return a context without tarsum
|
|
return archive.Generate(dockerfileName, string(dockerfile))
|
|
},
|
|
// fallback handler (tar context)
|
|
"": func(rc io.ReadCloser) (io.ReadCloser, error) {
|
|
return createProgressReader(rc), nil
|
|
},
|
|
})
|
|
default:
|
|
err = fmt.Errorf("remoteURL (%s) could not be recognized as URL", remoteURL)
|
|
}
|
|
return
|
|
}
|