|
@@ -1,235 +0,0 @@
|
|
-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/daemon"
|
|
|
|
- "github.com/docker/docker/image"
|
|
|
|
- "github.com/docker/docker/pkg/archive"
|
|
|
|
- "github.com/docker/docker/pkg/chrootarchive"
|
|
|
|
- "github.com/docker/docker/pkg/httputils"
|
|
|
|
- "github.com/docker/docker/pkg/idtools"
|
|
|
|
- "github.com/docker/docker/pkg/ioutils"
|
|
|
|
- "github.com/docker/docker/pkg/urlutil"
|
|
|
|
- "github.com/docker/docker/reference"
|
|
|
|
- "github.com/docker/docker/registry"
|
|
|
|
- "github.com/docker/engine-api/types"
|
|
|
|
- "github.com/docker/engine-api/types/container"
|
|
|
|
-)
|
|
|
|
-
|
|
|
|
-// Docker implements builder.Backend for the docker Daemon object.
|
|
|
|
-type Docker struct {
|
|
|
|
- *daemon.Daemon
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// ensure Docker implements builder.Backend
|
|
|
|
-var _ builder.Backend = Docker{}
|
|
|
|
-
|
|
|
|
-// Pull tells Docker to pull image referenced by `name`.
|
|
|
|
-func (d Docker) Pull(name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
|
|
|
|
- ref, err := reference.ParseNamed(name)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- ref = reference.WithDefaultTag(ref)
|
|
|
|
-
|
|
|
|
- pullRegistryAuth := &types.AuthConfig{}
|
|
|
|
- if len(authConfigs) > 0 {
|
|
|
|
- // The request came with a full auth config file, we prefer to use that
|
|
|
|
- repoInfo, err := d.Daemon.RegistryService.ResolveRepository(ref)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- resolvedConfig := registry.ResolveAuthConfig(
|
|
|
|
- authConfigs,
|
|
|
|
- repoInfo.Index,
|
|
|
|
- )
|
|
|
|
- pullRegistryAuth = &resolvedConfig
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := d.Daemon.PullImage(ref, nil, pullRegistryAuth, ioutils.NopWriteCloser(output)); err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- return d.GetImage(name)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// GetImage looks up a Docker image referenced by `name`.
|
|
|
|
-func (d Docker) GetImage(name string) (builder.Image, error) {
|
|
|
|
- img, err := d.Daemon.GetImage(name)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- return imgWrap{img}, nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// ContainerUpdateCmd updates Path and Args for the container with ID cID.
|
|
|
|
-func (d Docker) ContainerUpdateCmd(cID string, cmd []string) error {
|
|
|
|
- c, err := d.Daemon.GetContainer(cID)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- c.Path = cmd[0]
|
|
|
|
- c.Args = cmd[1:]
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// ContainerAttach attaches streams to the container cID. If stream is true, it streams the output.
|
|
|
|
-func (d Docker) ContainerAttach(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error {
|
|
|
|
- return d.Daemon.ContainerWsAttachWithLogs(cID, &daemon.ContainerWsAttachWithLogsConfig{
|
|
|
|
- InStream: stdin,
|
|
|
|
- OutStream: stdout,
|
|
|
|
- ErrStream: stderr,
|
|
|
|
- Stream: stream,
|
|
|
|
- })
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// BuilderCopy 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).
|
|
|
|
-// BuilderCopy should take in abstract paths (with slashes) and the implementation should convert it to OS-specific paths.
|
|
|
|
-func (d Docker) BuilderCopy(cID string, destPath string, src builder.FileInfo, decompress bool) error {
|
|
|
|
- srcPath := src.Path()
|
|
|
|
- destExists := true
|
|
|
|
- destDir := false
|
|
|
|
- rootUID, rootGID := d.Daemon.GetRemappedUIDGID()
|
|
|
|
-
|
|
|
|
- // Work in daemon-local OS specific file paths
|
|
|
|
- destPath = filepath.FromSlash(destPath)
|
|
|
|
-
|
|
|
|
- c, err := d.Daemon.GetContainer(cID)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- err = d.Daemon.Mount(c)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- defer d.Daemon.Unmount(c)
|
|
|
|
-
|
|
|
|
- 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 == "." {
|
|
|
|
- destDir = true
|
|
|
|
- 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
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- uidMaps, gidMaps := d.Daemon.GetUIDGIDMaps()
|
|
|
|
- archiver := &archive.Archiver{
|
|
|
|
- Untar: chrootarchive.Untar,
|
|
|
|
- UIDMaps: uidMaps,
|
|
|
|
- GIDMaps: gidMaps,
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if src.IsDir() {
|
|
|
|
- // copy as directory
|
|
|
|
- if err := archiver.CopyWithTar(srcPath, destPath); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- return fixPermissions(srcPath, destPath, rootUID, rootGID, destExists)
|
|
|
|
- }
|
|
|
|
- if decompress && archive.IsArchivePath(srcPath) {
|
|
|
|
- // 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
|
|
|
|
- err := archiver.UntarPath(srcPath, tarDest)
|
|
|
|
- if err != nil {
|
|
|
|
- logrus.Errorf("Couldn't untar to %s: %v", tarDest, err)
|
|
|
|
- }
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // only needed for fixPermissions, but might as well put it before CopyFileWithTar
|
|
|
|
- if destDir || (destExists && destStat.IsDir()) {
|
|
|
|
- destPath = filepath.Join(destPath, src.Name())
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := idtools.MkdirAllNewAs(filepath.Dir(destPath), 0755, rootUID, rootGID); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- if err := 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 *container.Config) (string, error) {
|
|
|
|
- cache, err := d.Daemon.ImageGetCached(image.ID(imgID), cfg)
|
|
|
|
- if cache == nil || err != nil {
|
|
|
|
- return "", err
|
|
|
|
- }
|
|
|
|
- return cache.ID().String(), nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// 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, createProgressReader func(in io.ReadCloser) io.ReadCloser) (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) {
|
|
|
|
- return createProgressReader(rc), nil
|
|
|
|
- },
|
|
|
|
- })
|
|
|
|
- default:
|
|
|
|
- err = fmt.Errorf("remoteURL (%s) could not be recognized as URL", remoteURL)
|
|
|
|
- }
|
|
|
|
- return
|
|
|
|
-}
|
|
|