1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/daemon/daemonbuilder/builder.go
Anusha Ragunathan 14215ed5a1 Make daemonbuilder.Docker leaner.
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>
2016-01-18 09:16:11 -08:00

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
}