Parcourir la source

Merge pull request #32092 from alfred-landrum/gdcaps

Let graphdrivers declare diff stream fidelity
Victor Vieux il y a 8 ans
Parent
commit
74093fe4ca

+ 17 - 0
daemon/graphdriver/driver.go

@@ -112,6 +112,23 @@ type Driver interface {
 	DiffDriver
 }
 
+// Capabilities defines a list of capabilities a driver may implement.
+// These capabilities are not required; however, they do determine how a
+// graphdriver can be used.
+type Capabilities struct {
+	// Flags that this driver is capable of reproducing exactly equivalent
+	// diffs for read-only layers. If set, clients can rely on the driver
+	// for consistent tar streams, and avoid extra processing to account
+	// for potential differences (eg: the layer store's use of tar-split).
+	ReproducesExactDiffs bool
+}
+
+// CapabilityDriver is the interface for layered file system drivers that
+// can report on their Capabilities.
+type CapabilityDriver interface {
+	Capabilities() Capabilities
+}
+
 // DiffGetterDriver is the interface for layered file system drivers that
 // provide a specialized function for getting file contents for tar-split.
 type DiffGetterDriver interface {

+ 1 - 1
daemon/graphdriver/plugin.go

@@ -38,6 +38,6 @@ func newPluginDriver(name string, pl plugingetter.CompatPlugin, config Options)
 			}
 		}
 	}
-	proxy := &graphDriverProxy{name, pl}
+	proxy := &graphDriverProxy{name, pl, Capabilities{}}
 	return proxy, proxy.Init(filepath.Join(home, name), config.DriverOptions, config.UIDMaps, config.GIDMaps)
 }

+ 30 - 7
daemon/graphdriver/proxy.go

@@ -9,11 +9,13 @@ import (
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/plugingetter"
+	"github.com/docker/docker/pkg/plugins"
 )
 
 type graphDriverProxy struct {
 	name string
 	p    plugingetter.CompatPlugin
+	caps Capabilities
 }
 
 type graphDriverRequest struct {
@@ -24,13 +26,14 @@ type graphDriverRequest struct {
 }
 
 type graphDriverResponse struct {
-	Err      string            `json:",omitempty"`
-	Dir      string            `json:",omitempty"`
-	Exists   bool              `json:",omitempty"`
-	Status   [][2]string       `json:",omitempty"`
-	Changes  []archive.Change  `json:",omitempty"`
-	Size     int64             `json:",omitempty"`
-	Metadata map[string]string `json:",omitempty"`
+	Err          string            `json:",omitempty"`
+	Dir          string            `json:",omitempty"`
+	Exists       bool              `json:",omitempty"`
+	Status       [][2]string       `json:",omitempty"`
+	Changes      []archive.Change  `json:",omitempty"`
+	Size         int64             `json:",omitempty"`
+	Metadata     map[string]string `json:",omitempty"`
+	Capabilities Capabilities      `json:",omitempty"`
 }
 
 type graphDriverInitRequest struct {
@@ -60,13 +63,33 @@ func (d *graphDriverProxy) Init(home string, opts []string, uidMaps, gidMaps []i
 	if ret.Err != "" {
 		return errors.New(ret.Err)
 	}
+	caps, err := d.fetchCaps()
+	if err != nil {
+		return err
+	}
+	d.caps = caps
 	return nil
 }
 
+func (d *graphDriverProxy) fetchCaps() (Capabilities, error) {
+	args := &graphDriverRequest{}
+	var ret graphDriverResponse
+	if err := d.p.Client().Call("GraphDriver.Capabilities", args, &ret); err != nil {
+		if !plugins.IsNotFound(err) {
+			return Capabilities{}, err
+		}
+	}
+	return ret.Capabilities, nil
+}
+
 func (d *graphDriverProxy) String() string {
 	return d.name
 }
 
+func (d *graphDriverProxy) Capabilities() Capabilities {
+	return d.caps
+}
+
 func (d *graphDriverProxy) CreateReadWrite(id, parent string, opts *CreateOpts) error {
 	return d.create("GraphDriver.CreateReadWrite", id, parent, opts)
 }

+ 23 - 0
docs/extend/plugins_graphdriver.md

@@ -84,6 +84,29 @@ The request also includes a list of UID and GID mappings, structed as follows:
 Respond with a non-empty string error if an error occurred.
 
 
+### /GraphDriver.Capabilities
+
+**Request**:
+```json
+{}
+```
+
+Get behavioral characteristics of the graph driver. If a plugin does not handle
+this request, the engine will use default values for all capabilities.
+
+**Response**:
+```json
+{
+  "ReproducesExactDiffs": false,
+}
+```
+
+Respond with values of capabilities:
+
+* **ReproducesExactDiffs** Defaults to false. Flags that this driver is capable
+of reproducing exactly equivalent diffs for read-only filesystem layers.
+
+
 ### /GraphDriver.Create
 
 **Request**:

+ 54 - 15
layer/layer_store.go

@@ -34,6 +34,8 @@ type layerStore struct {
 
 	mounts map[string]*mountedLayer
 	mountL sync.Mutex
+
+	useTarSplit bool
 }
 
 // StoreOptions are the options used to create a new Store instance
@@ -74,11 +76,17 @@ func NewStoreFromOptions(options StoreOptions) (Store, error) {
 // metadata store and graph driver. The metadata store will be used to restore
 // the Store.
 func NewStoreFromGraphDriver(store MetadataStore, driver graphdriver.Driver) (Store, error) {
+	caps := graphdriver.Capabilities{}
+	if capDriver, ok := driver.(graphdriver.CapabilityDriver); ok {
+		caps = capDriver.Capabilities()
+	}
+
 	ls := &layerStore{
-		store:    store,
-		driver:   driver,
-		layerMap: map[ChainID]*roLayer{},
-		mounts:   map[string]*mountedLayer{},
+		store:       store,
+		driver:      driver,
+		layerMap:    map[ChainID]*roLayer{},
+		mounts:      map[string]*mountedLayer{},
+		useTarSplit: !caps.ReproducesExactDiffs,
 	}
 
 	ids, mounts, err := store.List()
@@ -207,18 +215,21 @@ func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent stri
 	digester := digest.Canonical.Digester()
 	tr := io.TeeReader(ts, digester.Hash())
 
-	tsw, err := tx.TarSplitWriter(true)
-	if err != nil {
-		return err
-	}
-	metaPacker := storage.NewJSONPacker(tsw)
-	defer tsw.Close()
+	rdr := tr
+	if ls.useTarSplit {
+		tsw, err := tx.TarSplitWriter(true)
+		if err != nil {
+			return err
+		}
+		metaPacker := storage.NewJSONPacker(tsw)
+		defer tsw.Close()
 
-	// we're passing nil here for the file putter, because the ApplyDiff will
-	// handle the extraction of the archive
-	rdr, err := asm.NewInputTarStream(tr, metaPacker, nil)
-	if err != nil {
-		return err
+		// we're passing nil here for the file putter, because the ApplyDiff will
+		// handle the extraction of the archive
+		rdr, err = asm.NewInputTarStream(tr, metaPacker, nil)
+		if err != nil {
+			return err
+		}
 	}
 
 	applySize, err := ls.driver.ApplyDiff(layer.cacheID, parent, rdr)
@@ -640,6 +651,34 @@ func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc Mou
 	return initID, nil
 }
 
+func (ls *layerStore) getTarStream(rl *roLayer) (io.ReadCloser, error) {
+	if !ls.useTarSplit {
+		var parentCacheID string
+		if rl.parent != nil {
+			parentCacheID = rl.parent.cacheID
+		}
+
+		return ls.driver.Diff(rl.cacheID, parentCacheID)
+	}
+
+	r, err := ls.store.TarSplitReader(rl.chainID)
+	if err != nil {
+		return nil, err
+	}
+
+	pr, pw := io.Pipe()
+	go func() {
+		err := ls.assembleTarTo(rl.cacheID, r, nil, pw)
+		if err != nil {
+			pw.CloseWithError(err)
+		} else {
+			pw.Close()
+		}
+	}()
+
+	return pr, nil
+}
+
 func (ls *layerStore) assembleTarTo(graphID string, metadata io.ReadCloser, size *int64, w io.Writer) error {
 	diffDriver, ok := ls.driver.(graphdriver.DiffGetterDriver)
 	if !ok {

+ 3 - 12
layer/ro_layer.go

@@ -24,25 +24,16 @@ type roLayer struct {
 // TarStream for roLayer guarantees that the data that is produced is the exact
 // data that the layer was registered with.
 func (rl *roLayer) TarStream() (io.ReadCloser, error) {
-	r, err := rl.layerStore.store.TarSplitReader(rl.chainID)
+	rc, err := rl.layerStore.getTarStream(rl)
 	if err != nil {
 		return nil, err
 	}
 
-	pr, pw := io.Pipe()
-	go func() {
-		err := rl.layerStore.assembleTarTo(rl.cacheID, r, nil, pw)
-		if err != nil {
-			pw.CloseWithError(err)
-		} else {
-			pw.Close()
-		}
-	}()
-	rc, err := newVerifiedReadCloser(pr, digest.Digest(rl.diffID))
+	vrc, err := newVerifiedReadCloser(rc, digest.Digest(rl.diffID))
 	if err != nil {
 		return nil, err
 	}
-	return rc, nil
+	return vrc, nil
 }
 
 // TarStreamFrom does not make any guarantees to the correctness of the produced