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