35a22c9e12
To avoid an expensive call to archive.ChangesDirs() which walks two directory trees and compares every entry, archive.ApplyLayer() has been extended to also return the size of the layer changes. Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)
150 lines
3.8 KiB
Go
150 lines
3.8 KiB
Go
// +build daemon
|
|
|
|
package graphdriver
|
|
|
|
import (
|
|
"time"
|
|
|
|
log "github.com/Sirupsen/logrus"
|
|
"github.com/docker/docker/pkg/archive"
|
|
"github.com/docker/docker/pkg/chrootarchive"
|
|
"github.com/docker/docker/pkg/ioutils"
|
|
)
|
|
|
|
// naiveDiffDriver takes a ProtoDriver and adds the
|
|
// capability of the Diffing methods which it may or may not
|
|
// support on its own. See the comment on the exported
|
|
// NaiveDiffDriver function below.
|
|
// Notably, the AUFS driver doesn't need to be wrapped like this.
|
|
type naiveDiffDriver struct {
|
|
ProtoDriver
|
|
}
|
|
|
|
// NaiveDiffDriver returns a fully functional driver that wraps the
|
|
// given ProtoDriver and adds the capability of the following methods which
|
|
// it may or may not support on its own:
|
|
// Diff(id, parent string) (archive.Archive, error)
|
|
// Changes(id, parent string) ([]archive.Change, error)
|
|
// ApplyDiff(id, parent string, diff archive.ArchiveReader) (size int64, err error)
|
|
// DiffSize(id, parent string) (size int64, err error)
|
|
func NaiveDiffDriver(driver ProtoDriver) Driver {
|
|
return &naiveDiffDriver{ProtoDriver: driver}
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// 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) (size 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 size, err = chrootarchive.ApplyLayer(layerFs, diff); err != nil {
|
|
return
|
|
}
|
|
log.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
|
|
|
|
return
|
|
}
|
|
|
|
// DiffSize calculates the changes between the specified layer
|
|
// and its parent and returns the size in bytes of the changes
|
|
// relative to its base filesystem directory.
|
|
func (gdw *naiveDiffDriver) DiffSize(id, parent string) (size int64, err error) {
|
|
driver := gdw.ProtoDriver
|
|
|
|
changes, err := gdw.Changes(id, parent)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
layerFs, err := driver.Get(id, "")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer driver.Put(id)
|
|
|
|
return archive.ChangesSize(layerFs, changes), nil
|
|
}
|