Merge pull request #29869 from cpuguy83/backport_28263

[cherry-pick] Moves graphdriver plugin docs out of experimental
This commit is contained in:
Tibor Vass 2017-01-03 21:35:06 -08:00 committed by GitHub
commit 7d58f5fb88
11 changed files with 178 additions and 95 deletions

View file

@ -570,6 +570,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

View file

@ -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 <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)
@ -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

View file

@ -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 {

View file

@ -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)
}

View file

@ -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 {

View file

@ -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
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 fs 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
@ -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,18 +301,18 @@ 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": ""
}
```
Responds 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",
"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": ""

View file

@ -258,7 +258,7 @@ func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
}
func (s *DockerDaemonSuite) TestGraphdriverPlugin(c *check.C) {
testRequires(c, Network, IsAmd64, DaemonIsLinux, overlaySupported)
testRequires(c, Network, IsAmd64, DaemonIsLinux, overlay2Supported, ExperimentalDaemon)
s.d.Start()
@ -268,7 +268,7 @@ func (s *DockerDaemonSuite) TestGraphdriverPlugin(c *check.C) {
c.Assert(err, checker.IsNil, check.Commentf(out))
// restart the daemon with the plugin set as the storage driver
s.d.Restart("-s", plugin)
s.d.Restart("-s", plugin, "--storage-opt", "overlay2.override_kernel_check=1")
// run a container
out, err = s.d.Cmd("run", "--rm", "busybox", "true") // this will pull busybox using the plugin

View file

@ -79,6 +79,8 @@ var (
// daemonPid is the pid of the main test daemon
daemonPid int
daemonKernelVersion string
)
const (
@ -134,6 +136,7 @@ func init() {
type Info struct {
DockerRootDir string
ExperimentalBuild bool
KernelVersion string
}
var i Info
status, b, err := sockRequest("GET", "/info", nil)
@ -141,6 +144,7 @@ func init() {
if err = json.Unmarshal(b, &i); err == nil {
dockerBasePath = i.DockerRootDir
experimentalDaemon = i.ExperimentalBuild
daemonKernelVersion = i.KernelVersion
}
}
volumesConfigPath = dockerBasePath + "/volumes"

View file

@ -8,6 +8,7 @@ import (
"os/exec"
"strings"
"github.com/docker/docker/pkg/parsers/kernel"
"github.com/docker/docker/pkg/sysinfo"
)
@ -124,7 +125,7 @@ var (
},
"Test cannot be run without a kernel (4.3+) supporting ambient capabilities",
}
overlaySupported = testRequirement{
overlayFSSupported = testRequirement{
func() bool {
cmd := exec.Command(dockerBinary, "run", "--rm", "busybox", "/bin/sh", "-c", "cat /proc/filesystems")
out, err := cmd.CombinedOutput()
@ -133,7 +134,23 @@ var (
}
return bytes.Contains(out, []byte("overlay\n"))
},
"Test cannot be run wihtout suppport for ovelayfs",
"Test cannot be run without suppport for overlayfs",
}
overlay2Supported = testRequirement{
func() bool {
if !overlayFSSupported.Condition() {
return false
}
daemonV, err := kernel.ParseRelease(daemonKernelVersion)
if err != nil {
return false
}
requiredV := kernel.VersionInfo{Kernel: 4}
return kernel.CompareKernelVersion(*daemonV, requiredV) > -1
},
"Test cannot be run without overlay2 support (kernel 4.0+)",
}
)

View file

@ -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)
}

View file

@ -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()) {