123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- package evaluator
- func (b *buildFile) addContext(context io.Reader) (string, error) {
- tmpdirPath, err := ioutil.TempDir("", "docker-build")
- if err != nil {
- return err
- }
- decompressedStream, err := archive.DecompressStream(context)
- if err != nil {
- return err
- }
- b.context = &tarsum.TarSum{Reader: decompressedStream, DisableCompression: true}
- if err := archive.Untar(b.context, tmpdirPath, nil); err != nil {
- return err
- }
- b.contextPath = tmpdirPath
- return tmpdirPath
- }
- func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
- if b.image == "" {
- return fmt.Errorf("Please provide a source image with `from` prior to commit")
- }
- b.config.Image = b.image
- if id == "" {
- cmd := b.config.Cmd
- b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
- defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
- hit, err := b.probeCache()
- if err != nil {
- return err
- }
- if hit {
- return nil
- }
- container, warnings, err := b.daemon.Create(b.config, "")
- if err != nil {
- return err
- }
- for _, warning := range warnings {
- fmt.Fprintf(b.outStream, " ---> [Warning] %s\n", warning)
- }
- b.tmpContainers[container.ID] = struct{}{}
- fmt.Fprintf(b.outStream, " ---> Running in %s\n", utils.TruncateID(container.ID))
- id = container.ID
- if err := container.Mount(); err != nil {
- return err
- }
- defer container.Unmount()
- }
- container := b.daemon.Get(id)
- if container == nil {
- return fmt.Errorf("An error occured while creating the container")
- }
- // Note: Actually copy the struct
- autoConfig := *b.config
- autoConfig.Cmd = autoCmd
- // Commit the container
- image, err := b.daemon.Commit(container, "", "", "", b.maintainer, true, &autoConfig)
- if err != nil {
- return err
- }
- b.tmpImages[image.ID] = struct{}{}
- b.image = image.ID
- return nil
- }
- func (b *buildFile) runContextCommand(args string, allowRemote bool, allowDecompression bool, cmdName string) error {
- if b.context == nil {
- return fmt.Errorf("No context given. Impossible to use %s", cmdName)
- }
- tmp := strings.SplitN(args, " ", 2)
- if len(tmp) != 2 {
- return fmt.Errorf("Invalid %s format", cmdName)
- }
- orig, err := b.ReplaceEnvMatches(strings.Trim(tmp[0], " \t"))
- if err != nil {
- return err
- }
- dest, err := b.ReplaceEnvMatches(strings.Trim(tmp[1], " \t"))
- if err != nil {
- return err
- }
- cmd := b.config.Cmd
- b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, orig, dest)}
- defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
- b.config.Image = b.image
- var (
- origPath = orig
- destPath = dest
- remoteHash string
- isRemote bool
- decompress = true
- )
- isRemote = utils.IsURL(orig)
- if isRemote && !allowRemote {
- return fmt.Errorf("Source can't be an URL for %s", cmdName)
- } else if utils.IsURL(orig) {
- // Initiate the download
- resp, err := utils.Download(orig)
- if err != nil {
- return err
- }
- // Create a tmp dir
- tmpDirName, err := ioutil.TempDir(b.contextPath, "docker-remote")
- if err != nil {
- return err
- }
- // Create a tmp file within our tmp dir
- tmpFileName := path.Join(tmpDirName, "tmp")
- tmpFile, err := os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
- if err != nil {
- return err
- }
- defer os.RemoveAll(tmpDirName)
- // Download and dump result to tmp file
- if _, err := io.Copy(tmpFile, resp.Body); err != nil {
- tmpFile.Close()
- return err
- }
- tmpFile.Close()
- // Remove the mtime of the newly created tmp file
- if err := system.UtimesNano(tmpFileName, make([]syscall.Timespec, 2)); err != nil {
- return err
- }
- origPath = path.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName))
- // Process the checksum
- r, err := archive.Tar(tmpFileName, archive.Uncompressed)
- if err != nil {
- return err
- }
- tarSum := &tarsum.TarSum{Reader: r, DisableCompression: true}
- if _, err := io.Copy(ioutil.Discard, tarSum); err != nil {
- return err
- }
- remoteHash = tarSum.Sum(nil)
- r.Close()
- // If the destination is a directory, figure out the filename.
- if strings.HasSuffix(dest, "/") {
- u, err := url.Parse(orig)
- if err != nil {
- return err
- }
- path := u.Path
- if strings.HasSuffix(path, "/") {
- path = path[:len(path)-1]
- }
- parts := strings.Split(path, "/")
- filename := parts[len(parts)-1]
- if filename == "" {
- return fmt.Errorf("cannot determine filename from url: %s", u)
- }
- destPath = dest + filename
- }
- }
- if err := b.checkPathForAddition(origPath); err != nil {
- return err
- }
- // Hash path and check the cache
- if b.utilizeCache {
- var (
- hash string
- sums = b.context.GetSums()
- )
- if remoteHash != "" {
- hash = remoteHash
- } else if fi, err := os.Stat(path.Join(b.contextPath, origPath)); err != nil {
- return err
- } else if fi.IsDir() {
- var subfiles []string
- for file, sum := range sums {
- absFile := path.Join(b.contextPath, file)
- absOrigPath := path.Join(b.contextPath, origPath)
- if strings.HasPrefix(absFile, absOrigPath) {
- subfiles = append(subfiles, sum)
- }
- }
- sort.Strings(subfiles)
- hasher := sha256.New()
- hasher.Write([]byte(strings.Join(subfiles, ",")))
- hash = "dir:" + hex.EncodeToString(hasher.Sum(nil))
- } else {
- if origPath[0] == '/' && len(origPath) > 1 {
- origPath = origPath[1:]
- }
- origPath = strings.TrimPrefix(origPath, "./")
- if h, ok := sums[origPath]; ok {
- hash = "file:" + h
- }
- }
- b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, hash, dest)}
- hit, err := b.probeCache()
- if err != nil {
- return err
- }
- // If we do not have a hash, never use the cache
- if hit && hash != "" {
- return nil
- }
- }
- // Create the container
- container, _, err := b.daemon.Create(b.config, "")
- if err != nil {
- return err
- }
- b.tmpContainers[container.ID] = struct{}{}
- if err := container.Mount(); err != nil {
- return err
- }
- defer container.Unmount()
- if !allowDecompression || isRemote {
- decompress = false
- }
- if err := b.addContext(container, origPath, destPath, decompress); err != nil {
- return err
- }
- if err := b.commit(container.ID, cmd, fmt.Sprintf("%s %s in %s", cmdName, orig, dest)); err != nil {
- return err
- }
- return nil
- }
|