From 677fa03654886ee776ff478c30681d5376cfc196 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Sat, 19 Nov 2016 11:41:07 -0500 Subject: [PATCH] Moves graphdriver plugn docs out of experimental Also updates some of the structures being sent so plugins are getting all the new options. Signed-off-by: Brian Goff --- daemon/daemon.go | 1 + daemon/graphdriver/driver.go | 31 +++-- .../graphdriver/graphtest/graphtest_unix.go | 2 +- daemon/graphdriver/plugin.go | 14 +- daemon/graphdriver/proxy.go | 48 ++++--- .../extend}/plugins_graphdriver.md | 128 ++++++++++++------ .../docker_cli_daemon_plugins_test.go | 2 +- layer/layer_store.go | 15 +- layer/layer_test.go | 3 +- 9 files changed, 153 insertions(+), 91 deletions(-) rename {experimental => docs/extend}/plugins_graphdriver.md (75%) diff --git a/daemon/daemon.go b/daemon/daemon.go index 85a3b825b1..899f6e2810 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -568,6 +568,7 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot UIDMaps: uidMaps, GIDMaps: gidMaps, PluginGetter: d.PluginStore, + ExperimentalEnabled: config.Experimental, }) if err != nil { return nil, err diff --git a/daemon/graphdriver/driver.go b/daemon/graphdriver/driver.go index dc41d780be..f0bce562b7 100644 --- a/daemon/graphdriver/driver.go +++ b/daemon/graphdriver/driver.go @@ -150,16 +150,16 @@ func Register(name string, initFunc InitFunc) error { } // 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 { - 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 { 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 } @@ -172,15 +172,24 @@ func getBuiltinDriver(name, home string, options []string, uidMaps, gidMaps []id 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. -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 != "" { 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 - driversMap := scanPriorDrivers(root) + driversMap := scanPriorDrivers(config.Root) for _, name := range priority { if name == "vfs" { // 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 { // of the state found from prior drivers, check in order of our priority // 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 { // unlike below, we will return error here, because there is prior // 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) } - return nil, fmt.Errorf("%s contains several valid graphdrivers: %s; Please cleanup or explicitly choose storage driver (-s )", root, strings.Join(driversSlice, ", ")) + return nil, fmt.Errorf("%s contains several valid graphdrivers: %s; Please cleanup or explicitly choose storage driver (-s )", config.Root, strings.Join(driversSlice, ", ")) } 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 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 isDriverNotSupported(err) { 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 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 isDriverNotSupported(err) { continue diff --git a/daemon/graphdriver/graphtest/graphtest_unix.go b/daemon/graphdriver/graphtest/graphtest_unix.go index 39e1d2f183..6e952de78b 100644 --- a/daemon/graphdriver/graphtest/graphtest_unix.go +++ b/daemon/graphdriver/graphtest/graphtest_unix.go @@ -41,7 +41,7 @@ func newDriver(t testing.TB, name string, options []string) *Driver { 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 { t.Logf("graphdriver: %v\n", err) if err == graphdriver.ErrNotSupported || err == graphdriver.ErrPrerequisites || err == graphdriver.ErrIncompatibleFS { diff --git a/daemon/graphdriver/plugin.go b/daemon/graphdriver/plugin.go index 4f2f09d72f..7294bcc5f6 100644 --- a/daemon/graphdriver/plugin.go +++ b/daemon/graphdriver/plugin.go @@ -18,15 +18,19 @@ type pluginClient interface { 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 { 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 p, ok := pl.(*v2.Plugin); ok { if p.PropagatedMount != "" { @@ -35,5 +39,5 @@ func newPluginDriver(name, home string, opts []string, pl plugingetter.CompatPlu } } 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) } diff --git a/daemon/graphdriver/proxy.go b/daemon/graphdriver/proxy.go index f037ab736e..bfe74cc6f9 100644 --- a/daemon/graphdriver/proxy.go +++ b/daemon/graphdriver/proxy.go @@ -7,6 +7,7 @@ import ( "path/filepath" "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/plugingetter" ) @@ -16,9 +17,10 @@ type graphDriverProxy 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 { @@ -32,11 +34,13 @@ type graphDriverResponse 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 cp, ok := d.p.(plugingetter.CountedPlugin); ok { // 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{ - Home: home, - Opts: opts, + Home: home, + Opts: opts, + UIDMaps: uidMaps, + GIDMaps: gidMaps, } var ret graphDriverResponse 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 { - mountLabel := "" + args := &graphDriverRequest{ + ID: id, + Parent: parent, + } 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 if err := d.p.Client().Call("GraphDriver.CreateReadWrite", args, &ret); err != nil { 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 { - mountLabel := "" - if opts != nil { - mountLabel = opts.MountLabel - } 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 if err := d.p.Client().Call("GraphDriver.Create", args, &ret); err != nil { diff --git a/experimental/plugins_graphdriver.md b/docs/extend/plugins_graphdriver.md similarity index 75% rename from experimental/plugins_graphdriver.md rename to docs/extend/plugins_graphdriver.md index 2589ba38be..fdcb34d7b6 100644 --- a/experimental/plugins_graphdriver.md +++ b/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 +--- + + + + +## Changelog + +### 1.13.0 + +- Support v2 plugins + +# Docker graph driver plugins 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 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 + +$ pkill dockerd +$ dockerd --experimental -s cpuguy83/docker-overlay2-graphdriver-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 **Request**: -``` +```json { "Home": "/graph/home/path", - "Opts": [] + "Opts": [], + "UIDMaps": [], + "GIDMaps": [] } ``` 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. + +The request also includes a list of UID and GID mappings, structed as follows: +```json +{ + "ContainerID": 0, + "HostID": 0, + "Size": 0 +} +``` **Response**: -``` +```json { "Err": "" } @@ -47,20 +87,21 @@ Respond with a non-empty string error if an error occurred. ### /GraphDriver.Create **Request**: -``` +```json { "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187", - "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142" - "MountLabel": "" + "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142", + "MountLabel": "", + "StorageOpt": {} } ``` 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**: -``` +```json { "Err": "" } @@ -71,11 +112,12 @@ Respond with a non-empty string error if an error occurred. ### /GraphDriver.CreateReadWrite **Request**: -``` +```json { "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 **Request**: -``` +```json { "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`. **Response**: -``` +```json { "Err": "" } @@ -104,9 +146,9 @@ Respond with a non-empty string error if an error occurred. ### /GraphDriver.Get **Request**: -``` +```json { - "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187" + "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187", "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`. **Response**: -``` +```json { "Dir": "/var/mygraph/46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187", "Err": "" @@ -127,7 +169,7 @@ Respond with a non-empty string error if an error occurred. ### /GraphDriver.Put **Request**: -``` +```json { "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187" } @@ -137,7 +179,7 @@ Release the system resources for the specified `ID`, such as unmounting the filesystem layer. **Response**: -``` +```json { "Err": "" } @@ -148,7 +190,7 @@ Respond with a non-empty string error if an error occurred. ### /GraphDriver.Exists **Request**: -``` +```json { "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. **Response**: -``` +```json { "Exists": true } @@ -169,14 +211,14 @@ Respond with a boolean for whether or not the filesystem layer with the specifie ### /GraphDriver.Status **Request**: -``` +```json {} ``` Get low-level diagnostic information about the graph driver. **Response**: -``` +```json { "Status": [[]] } @@ -189,7 +231,7 @@ information. ### /GraphDriver.GetMetadata **Request**: -``` +```json { "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187" } @@ -199,7 +241,7 @@ Get low-level diagnostic information about the layered filesystem with the with the specified `ID` **Response**: -``` +```json { "Metadata": {}, "Err": "" @@ -213,15 +255,15 @@ Respond with a non-empty string error if an error occurred. ### /GraphDriver.Cleanup **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. **Response**: -``` +```json { "Err": "" } @@ -233,7 +275,7 @@ Respond with a non-empty string error if an error occurred. ### /GraphDriver.Diff **Request**: -``` +```json { "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187", "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142" @@ -251,7 +293,7 @@ and `Parent`. `Parent` may be an empty string, in which case there is no parent. ### /GraphDriver.Changes **Request**: -``` +```json { "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187", "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 -`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**: -``` +```json { "Changes": [{}], "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: -``` +```json "Path": "/some/path", "Kind": 0, ``` @@ -300,7 +342,7 @@ and `Parent` - parent (required)- the `Parent` of the given `ID` **Response**: -``` +```json { "Size": 512366, "Err": "" @@ -313,7 +355,7 @@ Respond with a non-empty string error if an error occurred. ### /GraphDriver.DiffSize **Request**: -``` +```json { "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187", "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142" @@ -323,7 +365,7 @@ Respond with a non-empty string error if an error occurred. Calculate the changes between the specified `ID` **Response**: -``` +```json { "Size": 512366, "Err": "" diff --git a/integration-cli/docker_cli_daemon_plugins_test.go b/integration-cli/docker_cli_daemon_plugins_test.go index cb4ce547e1..55fdd90dba 100644 --- a/integration-cli/docker_cli_daemon_plugins_test.go +++ b/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) { - testRequires(c, Network, IsAmd64, DaemonIsLinux, overlay2Supported) + testRequires(c, Network, IsAmd64, DaemonIsLinux, overlay2Supported, ExperimentalDaemon) s.d.Start(c) diff --git a/layer/layer_store.go b/layer/layer_store.go index ad70c0d324..4fc323f578 100644 --- a/layer/layer_store.go +++ b/layer/layer_store.go @@ -45,17 +45,18 @@ type StoreOptions struct { UIDMaps []idtools.IDMap GIDMaps []idtools.IDMap PluginGetter plugingetter.PluginGetter + ExperimentalEnabled bool } // NewStoreFromOptions creates a new Store instance 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 { return nil, fmt.Errorf("error initializing graphdriver: %v", err) } diff --git a/layer/layer_test.go b/layer/layer_test.go index c65dddd9e2..d2cc91332c 100644 --- a/layer/layer_test.go +++ b/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()) {