github.com/ncdc/docker@v0.10.1-0.20160129113957-6c6729ef5b74/daemon/daemonbuilder/builder.go (about)

     1  package daemonbuilder
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"github.com/Sirupsen/logrus"
    12  	"github.com/docker/docker/api"
    13  	"github.com/docker/docker/builder"
    14  	"github.com/docker/docker/daemon"
    15  	"github.com/docker/docker/image"
    16  	"github.com/docker/docker/pkg/archive"
    17  	"github.com/docker/docker/pkg/chrootarchive"
    18  	"github.com/docker/docker/pkg/httputils"
    19  	"github.com/docker/docker/pkg/idtools"
    20  	"github.com/docker/docker/pkg/ioutils"
    21  	"github.com/docker/docker/pkg/urlutil"
    22  	"github.com/docker/docker/reference"
    23  	"github.com/docker/docker/registry"
    24  	"github.com/docker/engine-api/types"
    25  	"github.com/docker/engine-api/types/container"
    26  )
    27  
    28  // Docker implements builder.Backend for the docker Daemon object.
    29  type Docker struct {
    30  	*daemon.Daemon
    31  }
    32  
    33  // ensure Docker implements builder.Backend
    34  var _ builder.Backend = Docker{}
    35  
    36  // Pull tells Docker to pull image referenced by `name`.
    37  func (d Docker) Pull(name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
    38  	ref, err := reference.ParseNamed(name)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  	ref = reference.WithDefaultTag(ref)
    43  
    44  	pullRegistryAuth := &types.AuthConfig{}
    45  	if len(authConfigs) > 0 {
    46  		// The request came with a full auth config file, we prefer to use that
    47  		repoInfo, err := d.Daemon.RegistryService.ResolveRepository(ref)
    48  		if err != nil {
    49  			return nil, err
    50  		}
    51  
    52  		resolvedConfig := registry.ResolveAuthConfig(
    53  			authConfigs,
    54  			repoInfo.Index,
    55  		)
    56  		pullRegistryAuth = &resolvedConfig
    57  	}
    58  
    59  	if err := d.Daemon.PullImage(ref, nil, pullRegistryAuth, ioutils.NopWriteCloser(output)); err != nil {
    60  		return nil, err
    61  	}
    62  	return d.GetImage(name)
    63  }
    64  
    65  // GetImage looks up a Docker image referenced by `name`.
    66  func (d Docker) GetImage(name string) (builder.Image, error) {
    67  	img, err := d.Daemon.GetImage(name)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	return imgWrap{img}, nil
    72  }
    73  
    74  // ContainerUpdateCmd updates Path and Args for the container with ID cID.
    75  func (d Docker) ContainerUpdateCmd(cID string, cmd []string) error {
    76  	c, err := d.Daemon.GetContainer(cID)
    77  	if err != nil {
    78  		return err
    79  	}
    80  	c.Path = cmd[0]
    81  	c.Args = cmd[1:]
    82  	return nil
    83  }
    84  
    85  // ContainerAttach attaches streams to the container cID. If stream is true, it streams the output.
    86  func (d Docker) ContainerAttach(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error {
    87  	return d.Daemon.ContainerWsAttachWithLogs(cID, &daemon.ContainerWsAttachWithLogsConfig{
    88  		InStream:  stdin,
    89  		OutStream: stdout,
    90  		ErrStream: stderr,
    91  		Stream:    stream,
    92  	})
    93  }
    94  
    95  // BuilderCopy copies/extracts a source FileInfo to a destination path inside a container
    96  // specified by a container object.
    97  // TODO: make sure callers don't unnecessarily convert destPath with filepath.FromSlash (Copy does it already).
    98  // BuilderCopy should take in abstract paths (with slashes) and the implementation should convert it to OS-specific paths.
    99  func (d Docker) BuilderCopy(cID string, destPath string, src builder.FileInfo, decompress bool) error {
   100  	srcPath := src.Path()
   101  	destExists := true
   102  	destDir := false
   103  	rootUID, rootGID := d.Daemon.GetRemappedUIDGID()
   104  
   105  	// Work in daemon-local OS specific file paths
   106  	destPath = filepath.FromSlash(destPath)
   107  
   108  	c, err := d.Daemon.GetContainer(cID)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	err = d.Daemon.Mount(c)
   113  	if err != nil {
   114  		return err
   115  	}
   116  	defer d.Daemon.Unmount(c)
   117  
   118  	dest, err := c.GetResourcePath(destPath)
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	// Preserve the trailing slash
   124  	// TODO: why are we appending another path separator if there was already one?
   125  	if strings.HasSuffix(destPath, string(os.PathSeparator)) || destPath == "." {
   126  		destDir = true
   127  		dest += string(os.PathSeparator)
   128  	}
   129  
   130  	destPath = dest
   131  
   132  	destStat, err := os.Stat(destPath)
   133  	if err != nil {
   134  		if !os.IsNotExist(err) {
   135  			logrus.Errorf("Error performing os.Stat on %s. %s", destPath, err)
   136  			return err
   137  		}
   138  		destExists = false
   139  	}
   140  
   141  	uidMaps, gidMaps := d.Daemon.GetUIDGIDMaps()
   142  	archiver := &archive.Archiver{
   143  		Untar:   chrootarchive.Untar,
   144  		UIDMaps: uidMaps,
   145  		GIDMaps: gidMaps,
   146  	}
   147  
   148  	if src.IsDir() {
   149  		// copy as directory
   150  		if err := archiver.CopyWithTar(srcPath, destPath); err != nil {
   151  			return err
   152  		}
   153  		return fixPermissions(srcPath, destPath, rootUID, rootGID, destExists)
   154  	}
   155  	if decompress && archive.IsArchivePath(srcPath) {
   156  		// Only try to untar if it is a file and that we've been told to decompress (when ADD-ing a remote file)
   157  
   158  		// First try to unpack the source as an archive
   159  		// to support the untar feature we need to clean up the path a little bit
   160  		// because tar is very forgiving.  First we need to strip off the archive's
   161  		// filename from the path but this is only added if it does not end in slash
   162  		tarDest := destPath
   163  		if strings.HasSuffix(tarDest, string(os.PathSeparator)) {
   164  			tarDest = filepath.Dir(destPath)
   165  		}
   166  
   167  		// try to successfully untar the orig
   168  		err := archiver.UntarPath(srcPath, tarDest)
   169  		if err != nil {
   170  			logrus.Errorf("Couldn't untar to %s: %v", tarDest, err)
   171  		}
   172  		return err
   173  	}
   174  
   175  	// only needed for fixPermissions, but might as well put it before CopyFileWithTar
   176  	if destDir || (destExists && destStat.IsDir()) {
   177  		destPath = filepath.Join(destPath, src.Name())
   178  	}
   179  
   180  	if err := idtools.MkdirAllNewAs(filepath.Dir(destPath), 0755, rootUID, rootGID); err != nil {
   181  		return err
   182  	}
   183  	if err := archiver.CopyFileWithTar(srcPath, destPath); err != nil {
   184  		return err
   185  	}
   186  
   187  	return fixPermissions(srcPath, destPath, rootUID, rootGID, destExists)
   188  }
   189  
   190  // GetCachedImage returns a reference to a cached image whose parent equals `parent`
   191  // and runconfig equals `cfg`. A cache miss is expected to return an empty ID and a nil error.
   192  func (d Docker) GetCachedImage(imgID string, cfg *container.Config) (string, error) {
   193  	cache, err := d.Daemon.ImageGetCached(image.ID(imgID), cfg)
   194  	if cache == nil || err != nil {
   195  		return "", err
   196  	}
   197  	return cache.ID().String(), nil
   198  }
   199  
   200  // Following is specific to builder contexts
   201  
   202  // DetectContextFromRemoteURL returns a context and in certain cases the name of the dockerfile to be used
   203  // irrespective of user input.
   204  // progressReader is only used if remoteURL is actually a URL (not empty, and not a Git endpoint).
   205  func DetectContextFromRemoteURL(r io.ReadCloser, remoteURL string, createProgressReader func(in io.ReadCloser) io.ReadCloser) (context builder.ModifiableContext, dockerfileName string, err error) {
   206  	switch {
   207  	case remoteURL == "":
   208  		context, err = builder.MakeTarSumContext(r)
   209  	case urlutil.IsGitURL(remoteURL):
   210  		context, err = builder.MakeGitContext(remoteURL)
   211  	case urlutil.IsURL(remoteURL):
   212  		context, err = builder.MakeRemoteContext(remoteURL, map[string]func(io.ReadCloser) (io.ReadCloser, error){
   213  			httputils.MimeTypes.TextPlain: func(rc io.ReadCloser) (io.ReadCloser, error) {
   214  				dockerfile, err := ioutil.ReadAll(rc)
   215  				if err != nil {
   216  					return nil, err
   217  				}
   218  
   219  				// dockerfileName is set to signal that the remote was interpreted as a single Dockerfile, in which case the caller
   220  				// should use dockerfileName as the new name for the Dockerfile, irrespective of any other user input.
   221  				dockerfileName = api.DefaultDockerfileName
   222  
   223  				// TODO: return a context without tarsum
   224  				return archive.Generate(dockerfileName, string(dockerfile))
   225  			},
   226  			// fallback handler (tar context)
   227  			"": func(rc io.ReadCloser) (io.ReadCloser, error) {
   228  				return createProgressReader(rc), nil
   229  			},
   230  		})
   231  	default:
   232  		err = fmt.Errorf("remoteURL (%s) could not be recognized as URL", remoteURL)
   233  	}
   234  	return
   235  }