func (c *DaemonClient) ImageBuild(ctx context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) { if c.imageName != "" { if len(options.Tags) != 1 || options.Tags[0] != c.imageName { return types.ImageBuildResponse{}, fmt.Errorf("expected image %q, got %v", c.imageName, options.Tags) } } if c.contextDir != "" { tmp, err := ioutil.TempDir("", "image-build-test") if err != nil { return types.ImageBuildResponse{}, err } if err := archive.Untar(context, tmp, nil); err != nil { return types.ImageBuildResponse{}, err } changes, err := archive.ChangesDirs(tmp, c.contextDir) if err != nil { return types.ImageBuildResponse{}, err } if len(changes) != c.changes { return types.ImageBuildResponse{}, fmt.Errorf("expected %d changes, got %v", c.changes, changes) } b, err := json.Marshal(c.message) if err != nil { return types.ImageBuildResponse{}, err } return types.ImageBuildResponse{ Body: ioutil.NopCloser(bytes.NewReader(b)), }, nil } return c.NopClient.ImageBuild(ctx, context, options) }
// ApplyDiff extracts the changeset from the given diff into the // layer with the specified id and parent, returning the size of the // new layer in bytes. func (gdw *naiveDiffDriver) ApplyDiff(id, parent string, diff archive.ArchiveReader) (bytes int64, err error) { driver := gdw.ProtoDriver // Mount the root filesystem so we can apply the diff/layer. layerFs, err := driver.Get(id, "") if err != nil { return } defer driver.Put(id) start := time.Now().UTC() log.Debugf("Start untar layer") if err = archive.ApplyLayer(layerFs, diff); err != nil { return } log.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds()) if parent == "" { return utils.TreeSize(layerFs) } parentFs, err := driver.Get(parent, "") if err != nil { err = fmt.Errorf("Driver %s failed to get image parent %s: %s", driver, parent, err) return } defer driver.Put(parent) changes, err := archive.ChangesDirs(layerFs, parentFs) if err != nil { return } return archive.ChangesSize(layerFs, changes), nil }
func diff(id, parent string) (diff archive.Archive, err error) { // create pod // start or replace pod glog.Infof("Diff between %s and %s", id, parent) layerFs := "/tmp/test1" if parent == "" { archive, err := archive.Tar(layerFs, archive.Uncompressed) if err != nil { return nil, err } return ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() return err }), nil } parentFs := "/tmp/test2" changes, err := archive.ChangesDirs(layerFs, parentFs) if err != nil { return nil, err } archive, err := archive.ExportChanges(layerFs, changes) if err != nil { return nil, err } return ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() return err }), nil }
// ApplyDiff applies the new layer on top of the root, if parent does not exist with will return a ErrApplyDiffFallback error. func (d *Driver) ApplyDiff(id string, parent string, diff archive.ArchiveReader) (size int64, err error) { dir := d.dir(id) if parent == "" { return 0, ErrApplyDiffFallback } parentRootDir := path.Join(d.dir(parent), "root") if _, err := os.Stat(parentRootDir); err != nil { return 0, ErrApplyDiffFallback } // We now know there is a parent, and it has a "root" directory containing // the full root filesystem. We can just hardlink it and apply the // layer. This relies on two things: // 1) ApplyDiff is only run once on a clean (no writes to upper layer) container // 2) ApplyDiff doesn't do any in-place writes to files (would break hardlinks) // These are all currently true and are not expected to break tmpRootDir, err := ioutil.TempDir(dir, "tmproot") if err != nil { return 0, err } defer func() { if err != nil { os.RemoveAll(tmpRootDir) } else { os.RemoveAll(path.Join(dir, "upper")) os.RemoveAll(path.Join(dir, "work")) os.RemoveAll(path.Join(dir, "merged")) os.RemoveAll(path.Join(dir, "lower-id")) } }() if err = copyDir(parentRootDir, tmpRootDir, copyHardlink); err != nil { return 0, err } start := time.Now().UTC() log.Debugf("Start untar layer") if err = chrootarchive.ApplyLayer(tmpRootDir, diff); err != nil { return 0, err } log.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds()) changes, err := archive.ChangesDirs(tmpRootDir, parentRootDir, nil) if err != nil { return 0, err } size = archive.ChangesSize(tmpRootDir, changes) rootDir := path.Join(dir, "root") if err := os.Rename(tmpRootDir, rootDir); err != nil { return 0, err } return }
func Diff(mainPath, parentPath string) (arch io.ReadCloser, err error) { changes, err := archive.ChangesDirs(mainPath, parentPath) if err != nil { return nil, err } return archive.ExportChanges(mainPath, changes, nil, nil) }
func compareDirectories(src string, dest string) error { changes, err := archive.ChangesDirs(dest, src) if err != nil { return err } if len(changes) > 0 { return fmt.Errorf("Unexpected differences after untar: %v", changes) } return nil }
// Diff produces an archive of the changes between the specified // layer and its parent layer which may be "". func (gdw *NaiveDiffDriver) Diff(id, parent string) (arch archive.Archive, err error) { startTime := time.Now() driver := gdw.ProtoDriver layerFs, err := driver.Get(id, "") if err != nil { return nil, err } defer func() { if err != nil { driver.Put(id) } }() if parent == "" { archive, err := archive.Tar(layerFs, archive.Uncompressed) if err != nil { return nil, err } return ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() driver.Put(id) return err }), nil } parentFs, err := driver.Get(parent, "") if err != nil { return nil, err } defer driver.Put(parent) changes, err := archive.ChangesDirs(layerFs, parentFs) if err != nil { return nil, err } archive, err := archive.ExportChanges(layerFs, changes, gdw.uidMaps, gdw.gidMaps) if err != nil { return nil, err } return ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() driver.Put(id) // NaiveDiffDriver compares file metadata with parent layers. Parent layers // are extracted from tar's with full second precision on modified time. // We need this hack here to make sure calls within same second receive // correct result. time.Sleep(startTime.Truncate(time.Second).Add(time.Second).Sub(time.Now())) return err }), nil }
func main() { flag.Usage = func() { fmt.Println("Produce a tar from comparing two directory paths. By default a demo tar is created of around 200 files (including hardlinks)") fmt.Printf("%s [OPTIONS]\n", os.Args[0]) flag.PrintDefaults() } flag.Parse() log.Out = os.Stderr if (len(os.Getenv("DEBUG")) > 0) || *flDebug { logrus.SetLevel(logrus.DebugLevel) } var newDir, oldDir string if len(*flNewDir) == 0 { var err error newDir, err = ioutil.TempDir("", "docker-test-newDir") if err != nil { log.Fatal(err) } defer os.RemoveAll(newDir) if _, err := prepareUntarSourceDirectory(100, newDir, true); err != nil { log.Fatal(err) } } else { newDir = *flNewDir } if len(*flOldDir) == 0 { oldDir, err := ioutil.TempDir("", "docker-test-oldDir") if err != nil { log.Fatal(err) } defer os.RemoveAll(oldDir) } else { oldDir = *flOldDir } changes, err := archive.ChangesDirs(newDir, oldDir) if err != nil { log.Fatal(err) } a, err := archive.ExportChanges(newDir, changes) if err != nil { log.Fatal(err) } defer a.Close() i, err := io.Copy(os.Stdout, a) if err != nil && err != io.EOF { log.Fatal(err) } fmt.Fprintf(os.Stderr, "wrote archive of %d bytes", i) }
// Diff produces an archive of the changes between the specified // layer and its parent layer which may be "". func (gdw *naiveDiffDriver) Diff(id, parent string) (arch archive.Archive, err error) { driver := gdw.ProtoDriver layerFs, err := driver.Get(id, "") if err != nil { return nil, err } defer func() { if err != nil { driver.Put(id) } }() if parent == "" { archive, err := archive.Tar(layerFs, archive.Uncompressed) if err != nil { return nil, err } return ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() driver.Put(id) return err }), nil } parentFs, err := driver.Get(parent, "") if err != nil { return nil, err } defer driver.Put(parent) changes, err := archive.ChangesDirs(layerFs, parentFs) if err != nil { return nil, err } archive, err := archive.ExportChanges(layerFs, changes) if err != nil { return nil, err } return ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() driver.Put(id) return err }), nil }
// Changes produces a list of changes between the specified layer // and its parent layer. If parent is "", then all changes will be ADD changes. func (gdw *naiveDiffDriver) Changes(id, parent string) ([]archive.Change, error) { driver := gdw.ProtoDriver layerFs, err := driver.Get(id, "") if err != nil { return nil, err } defer driver.Put(id) parentFs := "" if parent != "" { parentFs, err = driver.Get(parent, "") if err != nil { return nil, err } defer driver.Put(parent) } return archive.ChangesDirs(layerFs, parentFs) }
func (r *gitRepo) exportChangeSet(br branch) (archive.Archive, error) { currentBr, err := r.currentBranch() if err != nil { return nil, err } _, err = r.checkout(br) if err != nil { return nil, err } defer func() { r.checkout(currentBr) }() branches, err := r.branch() if err != nil { return nil, err } switch br.number() { case 0: changes, err := archive.ChangesDirs(r.Path, "") if err != nil { return nil, err } var curatedChanges []archive.Change for _, ch := range changes { if !strings.HasPrefix(ch.Path, "/.git") { curatedChanges = append(curatedChanges, ch) } } return archive.ExportChanges(r.Path, curatedChanges) default: parentBr := branches[br.number()-1] diff, _ := r.diff(parentBr, br) return exportChanges(r.Path, diff) } }