Przeglądaj źródła

Merge pull request #28623 from cpuguy83/update_graphdriver_docs

Ensure graphdriver only loads with experimental flag
Vincent Demeester 8 lat temu
rodzic
commit
d3e3a97cb2

+ 1 - 0
daemon/daemon.go

@@ -575,6 +575,7 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
 		UIDMaps:                   uidMaps,
 		UIDMaps:                   uidMaps,
 		GIDMaps:                   gidMaps,
 		GIDMaps:                   gidMaps,
 		PluginGetter:              d.PluginStore,
 		PluginGetter:              d.PluginStore,
+		ExperimentalEnabled:       config.Experimental,
 	})
 	})
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err

+ 20 - 11
daemon/graphdriver/driver.go

@@ -150,16 +150,16 @@ func Register(name string, initFunc InitFunc) error {
 }
 }
 
 
 // GetDriver initializes and returns the registered driver
 // GetDriver initializes and returns the registered driver
-func GetDriver(name, home string, options []string, uidMaps, gidMaps []idtools.IDMap, pg plugingetter.PluginGetter) (Driver, error) {
+func GetDriver(name string, pg plugingetter.PluginGetter, config Options) (Driver, error) {
 	if initFunc, exists := drivers[name]; exists {
 	if initFunc, exists := drivers[name]; exists {
-		return initFunc(filepath.Join(home, name), options, uidMaps, gidMaps)
+		return initFunc(filepath.Join(config.Root, name), config.DriverOptions, config.UIDMaps, config.GIDMaps)
 	}
 	}
 
 
-	pluginDriver, err := lookupPlugin(name, home, options, pg)
+	pluginDriver, err := lookupPlugin(name, pg, config)
 	if err == nil {
 	if err == nil {
 		return pluginDriver, nil
 		return pluginDriver, nil
 	}
 	}
-	logrus.WithError(err).WithField("driver", name).WithField("home-dir", home).Error("Failed to GetDriver graph")
+	logrus.WithError(err).WithField("driver", name).WithField("home-dir", config.Root).Error("Failed to GetDriver graph")
 	return nil, ErrNotSupported
 	return nil, ErrNotSupported
 }
 }
 
 
@@ -172,15 +172,24 @@ func getBuiltinDriver(name, home string, options []string, uidMaps, gidMaps []id
 	return nil, ErrNotSupported
 	return nil, ErrNotSupported
 }
 }
 
 
+// Options is used to initialize a graphdriver
+type Options struct {
+	Root                string
+	DriverOptions       []string
+	UIDMaps             []idtools.IDMap
+	GIDMaps             []idtools.IDMap
+	ExperimentalEnabled bool
+}
+
 // New creates the driver and initializes it at the specified root.
 // New creates the driver and initializes it at the specified root.
-func New(root, name string, options []string, uidMaps, gidMaps []idtools.IDMap, pg plugingetter.PluginGetter) (Driver, error) {
+func New(name string, pg plugingetter.PluginGetter, config Options) (Driver, error) {
 	if name != "" {
 	if name != "" {
 		logrus.Debugf("[graphdriver] trying provided driver: %s", name) // so the logs show specified driver
 		logrus.Debugf("[graphdriver] trying provided driver: %s", name) // so the logs show specified driver
-		return GetDriver(name, root, options, uidMaps, gidMaps, pg)
+		return GetDriver(name, pg, config)
 	}
 	}
 
 
 	// Guess for prior driver
 	// Guess for prior driver
-	driversMap := scanPriorDrivers(root)
+	driversMap := scanPriorDrivers(config.Root)
 	for _, name := range priority {
 	for _, name := range priority {
 		if name == "vfs" {
 		if name == "vfs" {
 			// don't use vfs even if there is state present.
 			// don't use vfs even if there is state present.
@@ -189,7 +198,7 @@ func New(root, name string, options []string, uidMaps, gidMaps []idtools.IDMap,
 		if _, prior := driversMap[name]; prior {
 		if _, prior := driversMap[name]; prior {
 			// of the state found from prior drivers, check in order of our priority
 			// of the state found from prior drivers, check in order of our priority
 			// which we would prefer
 			// which we would prefer
-			driver, err := getBuiltinDriver(name, root, options, uidMaps, gidMaps)
+			driver, err := getBuiltinDriver(name, config.Root, config.DriverOptions, config.UIDMaps, config.GIDMaps)
 			if err != nil {
 			if err != nil {
 				// unlike below, we will return error here, because there is prior
 				// unlike below, we will return error here, because there is prior
 				// state, and now it is no longer supported/prereq/compatible, so
 				// state, and now it is no longer supported/prereq/compatible, so
@@ -207,7 +216,7 @@ func New(root, name string, options []string, uidMaps, gidMaps []idtools.IDMap,
 					driversSlice = append(driversSlice, name)
 					driversSlice = append(driversSlice, name)
 				}
 				}
 
 
-				return nil, fmt.Errorf("%s contains several valid graphdrivers: %s; Please cleanup or explicitly choose storage driver (-s <DRIVER>)", root, strings.Join(driversSlice, ", "))
+				return nil, fmt.Errorf("%s contains several valid graphdrivers: %s; Please cleanup or explicitly choose storage driver (-s <DRIVER>)", config.Root, strings.Join(driversSlice, ", "))
 			}
 			}
 
 
 			logrus.Infof("[graphdriver] using prior storage driver: %s", name)
 			logrus.Infof("[graphdriver] using prior storage driver: %s", name)
@@ -217,7 +226,7 @@ func New(root, name string, options []string, uidMaps, gidMaps []idtools.IDMap,
 
 
 	// Check for priority drivers first
 	// Check for priority drivers first
 	for _, name := range priority {
 	for _, name := range priority {
-		driver, err := getBuiltinDriver(name, root, options, uidMaps, gidMaps)
+		driver, err := getBuiltinDriver(name, config.Root, config.DriverOptions, config.UIDMaps, config.GIDMaps)
 		if err != nil {
 		if err != nil {
 			if isDriverNotSupported(err) {
 			if isDriverNotSupported(err) {
 				continue
 				continue
@@ -229,7 +238,7 @@ func New(root, name string, options []string, uidMaps, gidMaps []idtools.IDMap,
 
 
 	// Check all registered drivers if no priority driver is found
 	// Check all registered drivers if no priority driver is found
 	for name, initFunc := range drivers {
 	for name, initFunc := range drivers {
-		driver, err := initFunc(filepath.Join(root, name), options, uidMaps, gidMaps)
+		driver, err := initFunc(filepath.Join(config.Root, name), config.DriverOptions, config.UIDMaps, config.GIDMaps)
 		if err != nil {
 		if err != nil {
 			if isDriverNotSupported(err) {
 			if isDriverNotSupported(err) {
 				continue
 				continue

+ 1 - 1
daemon/graphdriver/graphtest/graphtest_unix.go

@@ -41,7 +41,7 @@ func newDriver(t testing.TB, name string, options []string) *Driver {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	d, err := graphdriver.GetDriver(name, root, options, nil, nil, nil)
+	d, err := graphdriver.GetDriver(name, nil, graphdriver.Options{DriverOptions: options, Root: root})
 	if err != nil {
 	if err != nil {
 		t.Logf("graphdriver: %v\n", err)
 		t.Logf("graphdriver: %v\n", err)
 		if err == graphdriver.ErrNotSupported || err == graphdriver.ErrPrerequisites || err == graphdriver.ErrIncompatibleFS {
 		if err == graphdriver.ErrNotSupported || err == graphdriver.ErrPrerequisites || err == graphdriver.ErrIncompatibleFS {

+ 9 - 5
daemon/graphdriver/plugin.go

@@ -18,15 +18,19 @@ type pluginClient interface {
 	SendFile(string, io.Reader, interface{}) error
 	SendFile(string, io.Reader, interface{}) error
 }
 }
 
 
-func lookupPlugin(name, home string, opts []string, pg plugingetter.PluginGetter) (Driver, error) {
-	pl, err := pg.Get(name, "GraphDriver", plugingetter.LOOKUP)
+func lookupPlugin(name string, pg plugingetter.PluginGetter, config Options) (Driver, error) {
+	if !config.ExperimentalEnabled {
+		return nil, fmt.Errorf("graphdriver plugins are only supported with experimental mode")
+	}
+	pl, err := pg.Get(name, "GraphDriver", plugingetter.ACQUIRE)
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("Error looking up graphdriver plugin %s: %v", name, err)
 		return nil, fmt.Errorf("Error looking up graphdriver plugin %s: %v", name, err)
 	}
 	}
-	return newPluginDriver(name, home, opts, pl)
+	return newPluginDriver(name, pl, config)
 }
 }
 
 
-func newPluginDriver(name, home string, opts []string, pl plugingetter.CompatPlugin) (Driver, error) {
+func newPluginDriver(name string, pl plugingetter.CompatPlugin, config Options) (Driver, error) {
+	home := config.Root
 	if !pl.IsV1() {
 	if !pl.IsV1() {
 		if p, ok := pl.(*v2.Plugin); ok {
 		if p, ok := pl.(*v2.Plugin); ok {
 			if p.PropagatedMount != "" {
 			if p.PropagatedMount != "" {
@@ -35,5 +39,5 @@ func newPluginDriver(name, home string, opts []string, pl plugingetter.CompatPlu
 		}
 		}
 	}
 	}
 	proxy := &graphDriverProxy{name, pl}
 	proxy := &graphDriverProxy{name, pl}
-	return proxy, proxy.Init(filepath.Join(home, name), opts)
+	return proxy, proxy.Init(filepath.Join(home, name), config.DriverOptions, config.UIDMaps, config.GIDMaps)
 }
 }

+ 26 - 22
daemon/graphdriver/proxy.go

@@ -7,6 +7,7 @@ import (
 	"path/filepath"
 	"path/filepath"
 
 
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
+	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/plugingetter"
 )
 )
 
 
@@ -16,9 +17,10 @@ type graphDriverProxy struct {
 }
 }
 
 
 type graphDriverRequest struct {
 type graphDriverRequest struct {
-	ID         string `json:",omitempty"`
-	Parent     string `json:",omitempty"`
-	MountLabel string `json:",omitempty"`
+	ID         string            `json:",omitempty"`
+	Parent     string            `json:",omitempty"`
+	MountLabel string            `json:",omitempty"`
+	StorageOpt map[string]string `json:",omitempty"`
 }
 }
 
 
 type graphDriverResponse struct {
 type graphDriverResponse struct {
@@ -32,11 +34,13 @@ type graphDriverResponse struct {
 }
 }
 
 
 type graphDriverInitRequest struct {
 type graphDriverInitRequest struct {
-	Home string
-	Opts []string
+	Home    string
+	Opts    []string        `json:"Opts"`
+	UIDMaps []idtools.IDMap `json:"UIDMaps"`
+	GIDMaps []idtools.IDMap `json:"GIDMaps"`
 }
 }
 
 
-func (d *graphDriverProxy) Init(home string, opts []string) error {
+func (d *graphDriverProxy) Init(home string, opts []string, uidMaps, gidMaps []idtools.IDMap) error {
 	if !d.p.IsV1() {
 	if !d.p.IsV1() {
 		if cp, ok := d.p.(plugingetter.CountedPlugin); ok {
 		if cp, ok := d.p.(plugingetter.CountedPlugin); ok {
 			// always acquire here, it will be cleaned up on daemon shutdown
 			// always acquire here, it will be cleaned up on daemon shutdown
@@ -44,8 +48,10 @@ func (d *graphDriverProxy) Init(home string, opts []string) error {
 		}
 		}
 	}
 	}
 	args := &graphDriverInitRequest{
 	args := &graphDriverInitRequest{
-		Home: home,
-		Opts: opts,
+		Home:    home,
+		Opts:    opts,
+		UIDMaps: uidMaps,
+		GIDMaps: gidMaps,
 	}
 	}
 	var ret graphDriverResponse
 	var ret graphDriverResponse
 	if err := d.p.Client().Call("GraphDriver.Init", args, &ret); err != nil {
 	if err := d.p.Client().Call("GraphDriver.Init", args, &ret); err != nil {
@@ -62,16 +68,15 @@ func (d *graphDriverProxy) String() string {
 }
 }
 
 
 func (d *graphDriverProxy) CreateReadWrite(id, parent string, opts *CreateOpts) error {
 func (d *graphDriverProxy) CreateReadWrite(id, parent string, opts *CreateOpts) error {
-	mountLabel := ""
+	args := &graphDriverRequest{
+		ID:     id,
+		Parent: parent,
+	}
 	if opts != nil {
 	if opts != nil {
-		mountLabel = opts.MountLabel
+		args.MountLabel = opts.MountLabel
+		args.StorageOpt = opts.StorageOpt
 	}
 	}
 
 
-	args := &graphDriverRequest{
-		ID:         id,
-		Parent:     parent,
-		MountLabel: mountLabel,
-	}
 	var ret graphDriverResponse
 	var ret graphDriverResponse
 	if err := d.p.Client().Call("GraphDriver.CreateReadWrite", args, &ret); err != nil {
 	if err := d.p.Client().Call("GraphDriver.CreateReadWrite", args, &ret); err != nil {
 		return err
 		return err
@@ -83,14 +88,13 @@ func (d *graphDriverProxy) CreateReadWrite(id, parent string, opts *CreateOpts)
 }
 }
 
 
 func (d *graphDriverProxy) Create(id, parent string, opts *CreateOpts) error {
 func (d *graphDriverProxy) Create(id, parent string, opts *CreateOpts) error {
-	mountLabel := ""
-	if opts != nil {
-		mountLabel = opts.MountLabel
-	}
 	args := &graphDriverRequest{
 	args := &graphDriverRequest{
-		ID:         id,
-		Parent:     parent,
-		MountLabel: mountLabel,
+		ID:     id,
+		Parent: parent,
+	}
+	if opts != nil {
+		args.MountLabel = opts.MountLabel
+		args.StorageOpt = opts.StorageOpt
 	}
 	}
 	var ret graphDriverResponse
 	var ret graphDriverResponse
 	if err := d.p.Client().Call("GraphDriver.Create", args, &ret); err != nil {
 	if err := d.p.Client().Call("GraphDriver.Create", args, &ret); err != nil {

+ 85 - 43
experimental/plugins_graphdriver.md → docs/extend/plugins_graphdriver.md

@@ -1,12 +1,42 @@
-# Experimental: Docker graph driver plugins
+---
+title: "Graphdriver plugins"
+description: "How to manage image and container filesystems with external plugins"
+keywords: "Examples, Usage, storage, image, docker, data, graph, plugin, api"
+advisory: experimental
+---
+
+<!-- This file is maintained within the docker/docker Github
+     repository at https://github.com/docker/docker/. Make all
+     pull requests against that repo. If you see this file in
+     another repository, consider it read-only there, as it will
+     periodically be overwritten by the definitive file. Pull
+     requests which include edits to this file in other repositories
+     will be rejected.
+-->
+
+
+## Changelog
+
+### 1.13.0
+
+- Support v2 plugins
+
+# Docker graph driver plugins
 
 
 Docker graph driver plugins enable admins to use an external/out-of-process
 Docker graph driver plugins enable admins to use an external/out-of-process
 graph driver for use with Docker engine. This is an alternative to using the
 graph driver for use with Docker engine. This is an alternative to using the
 built-in storage drivers, such as aufs/overlay/devicemapper/btrfs.
 built-in storage drivers, such as aufs/overlay/devicemapper/btrfs.
 
 
-A graph driver plugin is used for image and container filesystem storage, as such
-the plugin must be started and available for connections prior to Docker Engine
-being started.
+You need to install and enable the plugin and then restart the Docker daemon
+before using the plugin. See the following example for the correct ordering
+of steps.
+
+```
+$ docker plugin install cpuguy83/docker-overlay2-graphdriver-plugin # this command also enables the driver
+<output supressed>
+$ pkill dockerd
+$ dockerd --experimental -s cpuguy83/docker-overlay2-graphdriver-plugin
+```
 
 
 # Write a graph driver plugin
 # Write a graph driver plugin
 
 
@@ -22,20 +52,30 @@ expected to provide the rootfs for containers as well as image layer storage.
 ### /GraphDriver.Init
 ### /GraphDriver.Init
 
 
 **Request**:
 **Request**:
-```
+```json
 {
 {
   "Home": "/graph/home/path",
   "Home": "/graph/home/path",
-  "Opts": []
+  "Opts": [],
+  "UIDMaps": [],
+  "GIDMaps": []
 }
 }
 ```
 ```
 
 
 Initialize the graph driver plugin with a home directory and array of options.
 Initialize the graph driver plugin with a home directory and array of options.
-Plugins are not required to accept these options as the Docker Engine does not
-require that the plugin use this path or options, they are only being passed
-through from the user.
+These are passed through from the user, but the plugin is not required to parse
+or honor them.
 
 
-**Response**:
+The request also includes a list of UID and GID mappings, structed as follows:
+```json
+{
+  "ContainerID": 0,
+  "HostID": 0,
+  "Size": 0
+}
 ```
 ```
+
+**Response**:
+```json
 {
 {
   "Err": ""
   "Err": ""
 }
 }
@@ -47,20 +87,21 @@ Respond with a non-empty string error if an error occurred.
 ### /GraphDriver.Create
 ### /GraphDriver.Create
 
 
 **Request**:
 **Request**:
-```
+```json
 {
 {
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
-  "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142"
-  "MountLabel": ""
+  "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142",
+  "MountLabel": "",
+  "StorageOpt": {}
 }
 }
 ```
 ```
 
 
 Create a new, empty, read-only filesystem layer with the specified
 Create a new, empty, read-only filesystem layer with the specified
-`ID`, `Parent` and `MountLabel`. `Parent` may be an empty string,
-which would indicate that there is no parent layer.
+`ID`, `Parent` and `MountLabel`. If `Parent` is an empty string, there is no
+parent layer. `StorageOpt` is map of strings which indicate storage options.
 
 
 **Response**:
 **Response**:
-```
+```json
 {
 {
   "Err": ""
   "Err": ""
 }
 }
@@ -71,11 +112,12 @@ Respond with a non-empty string error if an error occurred.
 ### /GraphDriver.CreateReadWrite
 ### /GraphDriver.CreateReadWrite
 
 
 **Request**:
 **Request**:
-```
+```json
 {
 {
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
-  "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142"
-  "MountLabel": ""
+  "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142",
+  "MountLabel": "",
+  "StorageOpt": {}
 }
 }
 ```
 ```
 
 
@@ -84,7 +126,7 @@ Similar to `/GraphDriver.Create` but creates a read-write filesystem layer.
 ### /GraphDriver.Remove
 ### /GraphDriver.Remove
 
 
 **Request**:
 **Request**:
-```
+```json
 {
 {
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187"
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187"
 }
 }
@@ -93,7 +135,7 @@ Similar to `/GraphDriver.Create` but creates a read-write filesystem layer.
 Remove the filesystem layer with this given `ID`.
 Remove the filesystem layer with this given `ID`.
 
 
 **Response**:
 **Response**:
-```
+```json
 {
 {
   "Err": ""
   "Err": ""
 }
 }
@@ -104,9 +146,9 @@ Respond with a non-empty string error if an error occurred.
 ### /GraphDriver.Get
 ### /GraphDriver.Get
 
 
 **Request**:
 **Request**:
-```
+```json
 {
 {
-  "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187"
+  "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
   "MountLabel": ""
   "MountLabel": ""
 }
 }
 ```
 ```
@@ -114,7 +156,7 @@ Respond with a non-empty string error if an error occurred.
 Get the mountpoint for the layered filesystem referred to by the given `ID`.
 Get the mountpoint for the layered filesystem referred to by the given `ID`.
 
 
 **Response**:
 **Response**:
-```
+```json
 {
 {
   "Dir": "/var/mygraph/46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
   "Dir": "/var/mygraph/46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
   "Err": ""
   "Err": ""
@@ -127,7 +169,7 @@ Respond with a non-empty string error if an error occurred.
 ### /GraphDriver.Put
 ### /GraphDriver.Put
 
 
 **Request**:
 **Request**:
-```
+```json
 {
 {
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187"
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187"
 }
 }
@@ -137,7 +179,7 @@ Release the system resources for the specified `ID`, such as unmounting the
 filesystem layer.
 filesystem layer.
 
 
 **Response**:
 **Response**:
-```
+```json
 {
 {
   "Err": ""
   "Err": ""
 }
 }
@@ -148,7 +190,7 @@ Respond with a non-empty string error if an error occurred.
 ### /GraphDriver.Exists
 ### /GraphDriver.Exists
 
 
 **Request**:
 **Request**:
-```
+```json
 {
 {
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187"
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187"
 }
 }
@@ -157,7 +199,7 @@ Respond with a non-empty string error if an error occurred.
 Determine if a filesystem layer with the specified `ID` exists.
 Determine if a filesystem layer with the specified `ID` exists.
 
 
 **Response**:
 **Response**:
-```
+```json
 {
 {
   "Exists": true
   "Exists": true
 }
 }
@@ -169,14 +211,14 @@ Respond with a boolean for whether or not the filesystem layer with the specifie
 ### /GraphDriver.Status
 ### /GraphDriver.Status
 
 
 **Request**:
 **Request**:
-```
+```json
 {}
 {}
 ```
 ```
 
 
 Get low-level diagnostic information about the graph driver.
 Get low-level diagnostic information about the graph driver.
 
 
 **Response**:
 **Response**:
-```
+```json
 {
 {
   "Status": [[]]
   "Status": [[]]
 }
 }
@@ -189,7 +231,7 @@ information.
 ### /GraphDriver.GetMetadata
 ### /GraphDriver.GetMetadata
 
 
 **Request**:
 **Request**:
-```
+```json
 {
 {
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187"
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187"
 }
 }
@@ -199,7 +241,7 @@ Get low-level diagnostic information about the layered filesystem with the
 with the specified `ID`
 with the specified `ID`
 
 
 **Response**:
 **Response**:
-```
+```json
 {
 {
   "Metadata": {},
   "Metadata": {},
   "Err": ""
   "Err": ""
@@ -213,15 +255,15 @@ Respond with a non-empty string error if an error occurred.
 ### /GraphDriver.Cleanup
 ### /GraphDriver.Cleanup
 
 
 **Request**:
 **Request**:
-```
+```json
 {}
 {}
 ```
 ```
 
 
-Perform necessary tasks to release resources help by the plugin, for example
+Perform necessary tasks to release resources help by the plugin, such as
 unmounting all the layered file systems.
 unmounting all the layered file systems.
 
 
 **Response**:
 **Response**:
-```
+```json
 {
 {
   "Err": ""
   "Err": ""
 }
 }
@@ -233,7 +275,7 @@ Respond with a non-empty string error if an error occurred.
 ### /GraphDriver.Diff
 ### /GraphDriver.Diff
 
 
 **Request**:
 **Request**:
-```
+```json
 {
 {
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
   "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142"
   "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142"
@@ -251,7 +293,7 @@ and `Parent`. `Parent` may be an empty string, in which case there is no parent.
 ### /GraphDriver.Changes
 ### /GraphDriver.Changes
 
 
 **Request**:
 **Request**:
-```
+```json
 {
 {
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
   "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142"
   "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142"
@@ -259,10 +301,10 @@ and `Parent`. `Parent` may be an empty string, in which case there is no parent.
 ```
 ```
 
 
 Get a list of changes between the filesystem layers specified by the `ID` and
 Get a list of changes between the filesystem layers specified by the `ID` and
-`Parent`. `Parent` may be an empty string, in which case there is no parent.
+`Parent`. If `Parent` is an empty string, there is no parent.
 
 
 **Response**:
 **Response**:
-```
+```json
 {
 {
   "Changes": [{}],
   "Changes": [{}],
   "Err": ""
   "Err": ""
@@ -270,7 +312,7 @@ Get a list of changes between the filesystem layers specified by the `ID` and
 ```
 ```
 
 
 Respond with a list of changes. The structure of a change is:
 Respond with a list of changes. The structure of a change is:
-```
+```json
   "Path": "/some/path",
   "Path": "/some/path",
   "Kind": 0,
   "Kind": 0,
 ```
 ```
@@ -300,7 +342,7 @@ and `Parent`
 - parent (required)- the `Parent` of the given `ID`
 - parent (required)- the `Parent` of the given `ID`
 
 
 **Response**:
 **Response**:
-```
+```json
 {
 {
   "Size": 512366,
   "Size": 512366,
   "Err": ""
   "Err": ""
@@ -313,7 +355,7 @@ Respond with a non-empty string error if an error occurred.
 ### /GraphDriver.DiffSize
 ### /GraphDriver.DiffSize
 
 
 **Request**:
 **Request**:
-```
+```json
 {
 {
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
   "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
   "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142"
   "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142"
@@ -323,7 +365,7 @@ Respond with a non-empty string error if an error occurred.
 Calculate the changes between the specified `ID`
 Calculate the changes between the specified `ID`
 
 
 **Response**:
 **Response**:
-```
+```json
 {
 {
   "Size": 512366,
   "Size": 512366,
   "Err": ""
   "Err": ""

+ 1 - 1
integration-cli/docker_cli_daemon_plugins_test.go

@@ -236,7 +236,7 @@ func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
 }
 }
 
 
 func (s *DockerDaemonSuite) TestGraphdriverPlugin(c *check.C) {
 func (s *DockerDaemonSuite) TestGraphdriverPlugin(c *check.C) {
-	testRequires(c, Network, IsAmd64, DaemonIsLinux, overlay2Supported)
+	testRequires(c, Network, IsAmd64, DaemonIsLinux, overlay2Supported, ExperimentalDaemon)
 
 
 	s.d.Start(c)
 	s.d.Start(c)
 
 

+ 8 - 7
layer/layer_store.go

@@ -45,17 +45,18 @@ type StoreOptions struct {
 	UIDMaps                   []idtools.IDMap
 	UIDMaps                   []idtools.IDMap
 	GIDMaps                   []idtools.IDMap
 	GIDMaps                   []idtools.IDMap
 	PluginGetter              plugingetter.PluginGetter
 	PluginGetter              plugingetter.PluginGetter
+	ExperimentalEnabled       bool
 }
 }
 
 
 // NewStoreFromOptions creates a new Store instance
 // NewStoreFromOptions creates a new Store instance
 func NewStoreFromOptions(options StoreOptions) (Store, error) {
 func NewStoreFromOptions(options StoreOptions) (Store, error) {
-	driver, err := graphdriver.New(
-		options.StorePath,
-		options.GraphDriver,
-		options.GraphDriverOptions,
-		options.UIDMaps,
-		options.GIDMaps,
-		options.PluginGetter)
+	driver, err := graphdriver.New(options.GraphDriver, options.PluginGetter, graphdriver.Options{
+		Root:                options.StorePath,
+		DriverOptions:       options.GraphDriverOptions,
+		UIDMaps:             options.UIDMaps,
+		GIDMaps:             options.GIDMaps,
+		ExperimentalEnabled: options.ExperimentalEnabled,
+	})
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("error initializing graphdriver: %v", err)
 		return nil, fmt.Errorf("error initializing graphdriver: %v", err)
 	}
 	}

+ 2 - 1
layer/layer_test.go

@@ -39,7 +39,8 @@ func newVFSGraphDriver(td string) (graphdriver.Driver, error) {
 		},
 		},
 	}
 	}
 
 
-	return graphdriver.GetDriver("vfs", td, nil, uidMap, gidMap, nil)
+	options := graphdriver.Options{Root: td, UIDMaps: uidMap, GIDMaps: gidMap}
+	return graphdriver.GetDriver("vfs", nil, options)
 }
 }
 
 
 func newTestGraphDriver(t *testing.T) (graphdriver.Driver, func()) {
 func newTestGraphDriver(t *testing.T) (graphdriver.Driver, func()) {