func (b *Builder) runContextCommand(args []string, allowRemote bool, allowLocalDecompression bool, cmdName string) error { if b.context == nil { return fmt.Errorf("No context given. Impossible to use %s", cmdName) } if len(args) < 2 { return fmt.Errorf("Invalid %s format - at least two arguments required", cmdName) } // Work in daemon-specific filepath semantics dest := filepath.FromSlash(args[len(args)-1]) // last one is always the dest b.runConfig.Image = b.image var infos []copyInfo // Loop through each src file and calculate the info we need to // do the copy (e.g. hash value if cached). Don't actually do // the copy until we've looked at all src files var err error for _, orig := range args[0 : len(args)-1] { var fi builder.FileInfo decompress := allowLocalDecompression if urlutil.IsURL(orig) { if !allowRemote { return fmt.Errorf("Source can't be a URL for %s", cmdName) } fi, err = b.download(orig) if err != nil { return err } defer os.RemoveAll(filepath.Dir(fi.Path())) decompress = false infos = append(infos, copyInfo{fi, decompress}) continue } // not a URL subInfos, err := b.calcCopyInfo(cmdName, orig, allowLocalDecompression, true) if err != nil { return err } infos = append(infos, subInfos...) } if len(infos) == 0 { return fmt.Errorf("No source files were specified") } if len(infos) > 1 && !strings.HasSuffix(dest, string(os.PathSeparator)) { return fmt.Errorf("When using %s with more than one source file, the destination must be a directory and end with a /", cmdName) } // For backwards compat, if there's just one info then use it as the // cache look-up string, otherwise hash 'em all into one var srcHash string var origPaths string if len(infos) == 1 { fi := infos[0].FileInfo origPaths = fi.Name() if hfi, ok := fi.(builder.Hashed); ok { srcHash = hfi.Hash() } } else { var hashs []string var origs []string for _, info := range infos { fi := info.FileInfo origs = append(origs, fi.Name()) if hfi, ok := fi.(builder.Hashed); ok { hashs = append(hashs, hfi.Hash()) } } hasher := sha256.New() hasher.Write([]byte(strings.Join(hashs, ","))) srcHash = "multi:" + hex.EncodeToString(hasher.Sum(nil)) origPaths = strings.Join(origs, " ") } cmd := b.runConfig.Cmd if runtime.GOOS != "windows" { b.runConfig.Cmd = stringutils.NewStrSlice("/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest)) } else { b.runConfig.Cmd = stringutils.NewStrSlice("cmd", "/S", "/C", fmt.Sprintf("REM (nop) %s %s in %s", cmdName, srcHash, dest)) } defer func(cmd *stringutils.StrSlice) { b.runConfig.Cmd = cmd }(cmd) if hit, err := b.probeCache(); err != nil { return err } else if hit { return nil } // Create the Pod podId := fmt.Sprintf("buildpod-%s", utils.RandStr(10, "alpha")) tempSrcDir := fmt.Sprintf("/var/run/hyper/temp/%s/", podId) if err := os.MkdirAll(tempSrcDir, 0755); err != nil { glog.Errorf(err.Error()) return err } if _, err := os.Stat(tempSrcDir); err != nil { glog.Errorf(err.Error()) return err } shellDir := fmt.Sprintf("/var/run/hyper/shell/%s/", podId) if err := os.MkdirAll(shellDir, 0755); err != nil { glog.Errorf(err.Error()) return err } copyshell, err1 := os.Create(shellDir + "/exec-copy.sh") if err1 != nil { glog.Errorf(err1.Error()) return err1 } fmt.Fprintf(copyshell, "#!/bin/sh\n") podString, err := MakeCopyPod(podId, b.image, b.runConfig.WorkingDir, tempSrcDir, dest, shellDir) if err != nil { return err } err = b.Hyperdaemon.CreatePod(podId, podString, false) if err != nil { return err } // Get the container var ( containerId string = "" container *daemon.Container ) ps, ok := b.Hyperdaemon.PodList.GetStatus(podId) if !ok { return fmt.Errorf("Cannot find pod %s", podId) } for _, i := range ps.Containers { containerId = i.Id } container, err = b.Daemon.Get(containerId) if err != nil { glog.Error(err.Error()) return err } /* container, _, err := b.docker.Create(b.runConfig, nil) if err != nil { return err } defer b.docker.Unmount(container) */ b.tmpPods[podId] = struct{}{} b.tmpContainers[container.ID] = struct{}{} comment := fmt.Sprintf("%s %s in %s", cmdName, origPaths, dest) // Twiddle the destination when its a relative path - meaning, make it // relative to the WORKINGDIR if !system.IsAbs(dest) { hasSlash := strings.HasSuffix(dest, string(os.PathSeparator)) dest = filepath.Join(string(os.PathSeparator), filepath.FromSlash(b.runConfig.WorkingDir), dest) // Make sure we preserve any trailing slash if hasSlash { dest += string(os.PathSeparator) } } for _, info := range infos { if err := b.docker.Copy(container, tempSrcDir, info.FileInfo, info.decompress); err != nil { return err } if strings.HasSuffix(dest, string(os.PathSeparator)) == true { fmt.Fprintf(copyshell, fmt.Sprintf("cp /tmp/src/%s %s\n", info.FileInfo.Name(), filepath.Join(dest, info.FileInfo.Name()))) } else { fmt.Fprintf(copyshell, fmt.Sprintf("cp /tmp/src/%s %s\n", info.FileInfo.Name(), dest)) } } fmt.Fprintf(copyshell, "umount /tmp/src/\n") fmt.Fprintf(copyshell, "umount /tmp/shell/\n") fmt.Fprintf(copyshell, "rm -rf /tmp/shell/\n") fmt.Fprintf(copyshell, "rm -rf /tmp/src/\n") copyshell.Close() // start or replace pod vm, ok := b.Hyperdaemon.VmList[b.Name] if !ok { glog.Warningf("can not find VM(%s)", b.Name) bo := &hypervisor.BootConfig{ CPU: 1, Memory: 512, Kernel: b.Hyperdaemon.Kernel, Initrd: b.Hyperdaemon.Initrd, Bios: b.Hyperdaemon.Bios, Cbfs: b.Hyperdaemon.Cbfs, Vbox: b.Hyperdaemon.VboxImage, } vm = b.Hyperdaemon.NewVm(b.Name, 1, 512, false, types.VM_KEEP_AFTER_FINISH) err = vm.Launch(bo) if err != nil { return err } b.Hyperdaemon.AddVm(vm) } if vm.Status == types.S_VM_IDLE { code, cause, err := b.Hyperdaemon.StartPod(podId, "", b.Name, nil, false, false, types.VM_KEEP_AFTER_FINISH, []*hypervisor.TtyIO{}) if err != nil { glog.Errorf("Code is %d, Cause is %s, %s", code, cause, err.Error()) b.Hyperdaemon.KillVm(b.Name) return err } vm = b.Hyperdaemon.VmList[b.Name] // wait for cmd finish Status, err := vm.GetResponseChan() if err != nil { glog.Error(err.Error()) return err } defer vm.ReleaseResponseChan(Status) var vmResponse *types.VmResponse for { vmResponse = <-Status if vmResponse.VmId == b.Name { if vmResponse.Code == types.E_POD_FINISHED { glog.Infof("Got E_POD_FINISHED code response") break } } } pod, ok := b.Hyperdaemon.PodList.Get(podId) if !ok { return fmt.Errorf("Cannot find pod %s", podId) } pod.SetVM(b.Name, vm) // release pod from VM glog.Warningf("start stop pod") code, cause, err = b.Hyperdaemon.StopPod(podId, "no") if err != nil { glog.Errorf("Code is %d, Cause is %s, %s", code, cause, err.Error()) b.Hyperdaemon.KillVm(b.Name) return err } glog.Warningf("stop pod finish") } else { glog.Errorf("Vm is not IDLE") return fmt.Errorf("Vm is not IDLE") } if err := b.commit(container.ID, cmd, comment); err != nil { return err } return nil }
// 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 == "." { destPath += string(os.PathSeparator) } 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, src.Name()) } 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) }