mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
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:
parent
b47c50cf11
commit
aa96c3176b
6 changed files with 128 additions and 35 deletions
|
@ -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 {
|
||||||
|
@ -31,6 +33,7 @@ type graphDriverResponse struct {
|
||||||
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,6 +215,8 @@ 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())
|
||||||
|
|
||||||
|
rdr := tr
|
||||||
|
if ls.useTarSplit {
|
||||||
tsw, err := tx.TarSplitWriter(true)
|
tsw, err := tx.TarSplitWriter(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -216,10 +226,11 @@ func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent stri
|
||||||
|
|
||||||
// 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)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue