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