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