Let graphdrivers declare diff stream fidelity

This allows graphdrivers to declare that they can reproduce the original
diff stream for a layer. If they do so, the layer store will not use
tar-split processing, but will still verify the digest on layer export.
This makes it easier to experiment with non-default diff formats.

Signed-off-by: Alfred Landrum <alfred.landrum@docker.com>
This commit is contained in:
Alfred Landrum 2017-03-20 11:38:17 -07:00
Родитель b47c50cf11
Коммит aa96c3176b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: C8DB14EF7641B383
6 изменённых файлов: 128 добавлений и 35 удалений

Просмотреть файл

@ -112,6 +112,23 @@ type Driver interface {
DiffDriver 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 // DiffGetterDriver is the interface for layered file system drivers that
// provide a specialized function for getting file contents for tar-split. // provide a specialized function for getting file contents for tar-split.
type DiffGetterDriver interface { type DiffGetterDriver interface {

Просмотреть файл

@ -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) return proxy, proxy.Init(filepath.Join(home, name), config.DriverOptions, config.UIDMaps, config.GIDMaps)
} }

Просмотреть файл

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

Просмотреть файл

@ -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. 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 ### /GraphDriver.Create
**Request**: **Request**:

Просмотреть файл

@ -34,6 +34,8 @@ type layerStore struct {
mounts map[string]*mountedLayer mounts map[string]*mountedLayer
mountL sync.Mutex mountL sync.Mutex
useTarSplit bool
} }
// StoreOptions are the options used to create a new Store instance // 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 // metadata store and graph driver. The metadata store will be used to restore
// the Store. // the Store.
func NewStoreFromGraphDriver(store MetadataStore, driver graphdriver.Driver) (Store, error) { func NewStoreFromGraphDriver(store MetadataStore, driver graphdriver.Driver) (Store, error) {
caps := graphdriver.Capabilities{}
if capDriver, ok := driver.(graphdriver.CapabilityDriver); ok {
caps = capDriver.Capabilities()
}
ls := &layerStore{ ls := &layerStore{
store: store, store: store,
driver: driver, driver: driver,
layerMap: map[ChainID]*roLayer{}, layerMap: map[ChainID]*roLayer{},
mounts: map[string]*mountedLayer{}, mounts: map[string]*mountedLayer{},
useTarSplit: !caps.ReproducesExactDiffs,
} }
ids, mounts, err := store.List() ids, mounts, err := store.List()
@ -207,18 +215,21 @@ func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent stri
digester := digest.Canonical.Digester() digester := digest.Canonical.Digester()
tr := io.TeeReader(ts, digester.Hash()) tr := io.TeeReader(ts, digester.Hash())
tsw, err := tx.TarSplitWriter(true) rdr := tr
if err != nil { if ls.useTarSplit {
return err tsw, err := tx.TarSplitWriter(true)
} if err != nil {
metaPacker := storage.NewJSONPacker(tsw) return err
defer tsw.Close() }
metaPacker := storage.NewJSONPacker(tsw)
defer tsw.Close()
// we're passing nil here for the file putter, because the ApplyDiff will // we're passing nil here for the file putter, because the ApplyDiff will
// handle the extraction of the archive // handle the extraction of the archive
rdr, err := asm.NewInputTarStream(tr, metaPacker, nil) rdr, err = asm.NewInputTarStream(tr, metaPacker, nil)
if err != nil { if err != nil {
return err return err
}
} }
applySize, err := ls.driver.ApplyDiff(layer.cacheID, parent, rdr) 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 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 { func (ls *layerStore) assembleTarTo(graphID string, metadata io.ReadCloser, size *int64, w io.Writer) error {
diffDriver, ok := ls.driver.(graphdriver.DiffGetterDriver) diffDriver, ok := ls.driver.(graphdriver.DiffGetterDriver)
if !ok { if !ok {

Просмотреть файл

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