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 }