123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- 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/cliconfig"
- "github.com/docker/docker/daemon"
- "github.com/docker/docker/graph"
- "github.com/docker/docker/image"
- "github.com/docker/docker/pkg/archive"
- "github.com/docker/docker/pkg/httputils"
- "github.com/docker/docker/pkg/idtools"
- "github.com/docker/docker/pkg/ioutils"
- "github.com/docker/docker/pkg/parsers"
- "github.com/docker/docker/pkg/progressreader"
- "github.com/docker/docker/pkg/urlutil"
- "github.com/docker/docker/registry"
- "github.com/docker/docker/runconfig"
- )
- // Docker implements builder.Docker for the docker Daemon object.
- type Docker struct {
- Daemon *daemon.Daemon
- OutOld io.Writer
- AuthConfigs map[string]cliconfig.AuthConfig
- Archiver *archive.Archiver
- }
- // ensure Docker implements builder.Docker
- var _ builder.Docker = Docker{}
- // LookupImage looks up a Docker image referenced by `name`.
- func (d Docker) LookupImage(name string) (*image.Image, error) {
- return d.Daemon.GetImage(name)
- }
- // Pull tells Docker to pull image referenced by `name`.
- func (d Docker) Pull(name string) (*image.Image, error) {
- remote, tag := parsers.ParseRepositoryTag(name)
- if tag == "" {
- tag = "latest"
- }
- pullRegistryAuth := &cliconfig.AuthConfig{}
- if len(d.AuthConfigs) > 0 {
- // The request came with a full auth config file, we prefer to use that
- repoInfo, err := d.Daemon.RegistryService.ResolveRepository(remote)
- if err != nil {
- return nil, err
- }
- resolvedConfig := registry.ResolveAuthConfig(
- &cliconfig.ConfigFile{AuthConfigs: d.AuthConfigs},
- repoInfo.Index,
- )
- pullRegistryAuth = &resolvedConfig
- }
- imagePullConfig := &graph.ImagePullConfig{
- AuthConfig: pullRegistryAuth,
- OutStream: ioutils.NopWriteCloser(d.OutOld),
- }
- if err := d.Daemon.PullImage(remote, tag, imagePullConfig); err != nil {
- return nil, err
- }
- return d.Daemon.GetImage(name)
- }
- // Container looks up a Docker container referenced by `id`.
- func (d Docker) Container(id string) (*daemon.Container, error) {
- return d.Daemon.Get(id)
- }
- // Create creates a new Docker container and returns potential warnings
- func (d Docker) Create(cfg *runconfig.Config, hostCfg *runconfig.HostConfig) (*daemon.Container, []string, error) {
- ccr, err := d.Daemon.ContainerCreate(&daemon.ContainerCreateConfig{
- Name: "",
- Config: cfg,
- HostConfig: hostCfg,
- AdjustCPUShares: true,
- })
- if err != nil {
- return nil, nil, err
- }
- container, err := d.Daemon.Get(ccr.ID)
- if err != nil {
- return nil, ccr.Warnings, err
- }
- return container, ccr.Warnings, d.Mount(container)
- }
- // Remove removes a container specified by `id`.
- func (d Docker) Remove(id string, cfg *daemon.ContainerRmConfig) error {
- return d.Daemon.ContainerRm(id, cfg)
- }
- // Commit creates a new Docker image from an existing Docker container.
- func (d Docker) Commit(name string, cfg *daemon.ContainerCommitConfig) (*image.Image, error) {
- return d.Daemon.Commit(name, cfg)
- }
- // Retain retains an image avoiding it to be removed or overwritten until a corresponding Release() call.
- func (d Docker) Retain(sessionID, imgID string) {
- d.Daemon.Graph().Retain(sessionID, imgID)
- }
- // Release releases a list of images that were retained for the time of a build.
- func (d Docker) Release(sessionID string, activeImages []string) {
- d.Daemon.Graph().Release(sessionID, activeImages...)
- }
- // Copy 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).
- // Copy should take in abstract paths (with slashes) and the implementation should convert it to OS-specific paths.
- func (d Docker) Copy(c *daemon.Container, destPath string, src builder.FileInfo, decompress bool) error {
- srcPath := src.Path()
- destExists := true
- rootUID, rootGID := d.Daemon.GetRemappedUIDGID()
- // Work in daemon-local OS specific file paths
- destPath = filepath.FromSlash(destPath)
- 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 == "." {
- 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
- }
- if src.IsDir() {
- // copy as directory
- if err := d.Archiver.CopyWithTar(srcPath, destPath); err != nil {
- return err
- }
- return fixPermissions(srcPath, destPath, rootUID, rootGID, destExists)
- }
- if decompress {
- // 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
- if err := d.Archiver.UntarPath(srcPath, tarDest); err == nil {
- return nil
- } else if err != io.EOF {
- logrus.Debugf("Couldn't untar to %s: %v", tarDest, err)
- }
- }
- // only needed for fixPermissions, but might as well put it before CopyFileWithTar
- if destExists && destStat.IsDir() {
- destPath = filepath.Join(destPath, filepath.Base(srcPath))
- }
- if err := idtools.MkdirAllNewAs(filepath.Dir(destPath), 0755, rootUID, rootGID); err != nil {
- return err
- }
- if err := d.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 *runconfig.Config) (string, error) {
- cache, err := d.Daemon.ImageGetCached(imgID, cfg)
- if cache == nil || err != nil {
- return "", err
- }
- return cache.ID, nil
- }
- // Kill stops the container execution abruptly.
- func (d Docker) Kill(container *daemon.Container) error {
- return d.Daemon.Kill(container)
- }
- // Mount mounts the root filesystem for the container.
- func (d Docker) Mount(c *daemon.Container) error {
- return d.Daemon.Mount(c)
- }
- // Unmount unmounts the root filesystem for the container.
- func (d Docker) Unmount(c *daemon.Container) error {
- return d.Daemon.Unmount(c)
- }
- // Start starts a container
- func (d Docker) Start(c *daemon.Container) error {
- return d.Daemon.Start(c)
- }
- // 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, progressReader *progressreader.Config) (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) {
- progressReader.In = rc
- return progressReader, nil
- },
- })
- default:
- err = fmt.Errorf("remoteURL (%s) could not be recognized as URL", remoteURL)
- }
- return
- }
|