Browse code

Promote volume drivers from experimental to master.

Remove volume stubs and use the experimental path as the only path.

Signed-off-by: David Calavera <david.calavera@gmail.com>

David Calavera authored on 2015/07/16 03:25:56
Showing 17 changed files
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"github.com/docker/docker/pkg/system"
13 13
 	"github.com/docker/docker/runconfig"
14 14
 	"github.com/docker/docker/volume"
15
+	"github.com/docker/docker/volume/drivers"
15 16
 	"github.com/docker/docker/volume/local"
16 17
 	"github.com/opencontainers/runc/libcontainer/label"
17 18
 )
... ...
@@ -333,3 +334,18 @@ func removeVolume(v volume.Volume) error {
333 333
 	}
334 334
 	return vd.Remove(v)
335 335
 }
336
+
337
+func getVolumeDriver(name string) (volume.Driver, error) {
338
+	if name == "" {
339
+		name = volume.DefaultDriverName
340
+	}
341
+	return volumedrivers.Lookup(name)
342
+}
343
+
344
+func parseVolumeSource(spec string) (string, string, error) {
345
+	if !filepath.IsAbs(spec) {
346
+		return spec, "", nil
347
+	}
348
+
349
+	return "", spec, nil
350
+}
336 351
deleted file mode 100644
... ...
@@ -1,25 +0,0 @@
1
-// +build experimental
2
-
3
-package daemon
4
-
5
-import (
6
-	"path/filepath"
7
-
8
-	"github.com/docker/docker/volume"
9
-	"github.com/docker/docker/volume/drivers"
10
-)
11
-
12
-func getVolumeDriver(name string) (volume.Driver, error) {
13
-	if name == "" {
14
-		name = volume.DefaultDriverName
15
-	}
16
-	return volumedrivers.Lookup(name)
17
-}
18
-
19
-func parseVolumeSource(spec string) (string, string, error) {
20
-	if !filepath.IsAbs(spec) {
21
-		return spec, "", nil
22
-	}
23
-
24
-	return "", spec, nil
25
-}
26 1
deleted file mode 100644
... ...
@@ -1,87 +0,0 @@
1
-// +build experimental
2
-
3
-package daemon
4
-
5
-import (
6
-	"testing"
7
-
8
-	"github.com/docker/docker/runconfig"
9
-	"github.com/docker/docker/volume"
10
-	"github.com/docker/docker/volume/drivers"
11
-)
12
-
13
-type fakeDriver struct{}
14
-
15
-func (fakeDriver) Name() string                              { return "fake" }
16
-func (fakeDriver) Create(name string) (volume.Volume, error) { return nil, nil }
17
-func (fakeDriver) Remove(v volume.Volume) error              { return nil }
18
-
19
-func TestGetVolumeDriver(t *testing.T) {
20
-	_, err := getVolumeDriver("missing")
21
-	if err == nil {
22
-		t.Fatal("Expected error, was nil")
23
-	}
24
-
25
-	volumedrivers.Register(fakeDriver{}, "fake")
26
-	d, err := getVolumeDriver("fake")
27
-	if err != nil {
28
-		t.Fatal(err)
29
-	}
30
-	if d.Name() != "fake" {
31
-		t.Fatalf("Expected fake driver, got %s\n", d.Name())
32
-	}
33
-}
34
-
35
-func TestParseBindMount(t *testing.T) {
36
-	cases := []struct {
37
-		bind       string
38
-		driver     string
39
-		expDest    string
40
-		expSource  string
41
-		expName    string
42
-		expDriver  string
43
-		mountLabel string
44
-		expRW      bool
45
-		fail       bool
46
-	}{
47
-		{"/tmp:/tmp", "", "/tmp", "/tmp", "", "", "", true, false},
48
-		{"/tmp:/tmp:ro", "", "/tmp", "/tmp", "", "", "", false, false},
49
-		{"/tmp:/tmp:rw", "", "/tmp", "/tmp", "", "", "", true, false},
50
-		{"/tmp:/tmp:foo", "", "/tmp", "/tmp", "", "", "", false, true},
51
-		{"name:/tmp", "", "/tmp", "", "name", "local", "", true, false},
52
-		{"name:/tmp", "external", "/tmp", "", "name", "external", "", true, false},
53
-		{"name:/tmp:ro", "local", "/tmp", "", "name", "local", "", false, false},
54
-		{"local/name:/tmp:rw", "", "/tmp", "", "local/name", "local", "", true, false},
55
-	}
56
-
57
-	for _, c := range cases {
58
-		conf := &runconfig.Config{VolumeDriver: c.driver}
59
-		m, err := parseBindMount(c.bind, c.mountLabel, conf)
60
-		if c.fail {
61
-			if err == nil {
62
-				t.Fatalf("Expected error, was nil, for spec %s\n", c.bind)
63
-			}
64
-			continue
65
-		}
66
-
67
-		if m.Destination != c.expDest {
68
-			t.Fatalf("Expected destination %s, was %s, for spec %s\n", c.expDest, m.Destination, c.bind)
69
-		}
70
-
71
-		if m.Source != c.expSource {
72
-			t.Fatalf("Expected source %s, was %s, for spec %s\n", c.expSource, m.Source, c.bind)
73
-		}
74
-
75
-		if m.Name != c.expName {
76
-			t.Fatalf("Expected name %s, was %s for spec %s\n", c.expName, m.Name, c.bind)
77
-		}
78
-
79
-		if m.Driver != c.expDriver {
80
-			t.Fatalf("Expected driver %s, was %s, for spec %s\n", c.expDriver, m.Driver, c.bind)
81
-		}
82
-
83
-		if m.RW != c.expRW {
84
-			t.Fatalf("Expected RW %v, was %v for spec %s\n", c.expRW, m.RW, c.bind)
85
-		}
86
-	}
87
-}
88 1
new file mode 100644
... ...
@@ -0,0 +1,87 @@
0
+// +build experimental
1
+
2
+package daemon
3
+
4
+import (
5
+	"testing"
6
+
7
+	"github.com/docker/docker/runconfig"
8
+	"github.com/docker/docker/volume"
9
+	"github.com/docker/docker/volume/drivers"
10
+)
11
+
12
+type fakeDriver struct{}
13
+
14
+func (fakeDriver) Name() string                              { return "fake" }
15
+func (fakeDriver) Create(name string) (volume.Volume, error) { return nil, nil }
16
+func (fakeDriver) Remove(v volume.Volume) error              { return nil }
17
+
18
+func TestGetVolumeDriver(t *testing.T) {
19
+	_, err := getVolumeDriver("missing")
20
+	if err == nil {
21
+		t.Fatal("Expected error, was nil")
22
+	}
23
+
24
+	volumedrivers.Register(fakeDriver{}, "fake")
25
+	d, err := getVolumeDriver("fake")
26
+	if err != nil {
27
+		t.Fatal(err)
28
+	}
29
+	if d.Name() != "fake" {
30
+		t.Fatalf("Expected fake driver, got %s\n", d.Name())
31
+	}
32
+}
33
+
34
+func TestParseBindMount(t *testing.T) {
35
+	cases := []struct {
36
+		bind       string
37
+		driver     string
38
+		expDest    string
39
+		expSource  string
40
+		expName    string
41
+		expDriver  string
42
+		mountLabel string
43
+		expRW      bool
44
+		fail       bool
45
+	}{
46
+		{"/tmp:/tmp", "", "/tmp", "/tmp", "", "", "", true, false},
47
+		{"/tmp:/tmp:ro", "", "/tmp", "/tmp", "", "", "", false, false},
48
+		{"/tmp:/tmp:rw", "", "/tmp", "/tmp", "", "", "", true, false},
49
+		{"/tmp:/tmp:foo", "", "/tmp", "/tmp", "", "", "", false, true},
50
+		{"name:/tmp", "", "/tmp", "", "name", "local", "", true, false},
51
+		{"name:/tmp", "external", "/tmp", "", "name", "external", "", true, false},
52
+		{"name:/tmp:ro", "local", "/tmp", "", "name", "local", "", false, false},
53
+		{"local/name:/tmp:rw", "", "/tmp", "", "local/name", "local", "", true, false},
54
+	}
55
+
56
+	for _, c := range cases {
57
+		conf := &runconfig.Config{VolumeDriver: c.driver}
58
+		m, err := parseBindMount(c.bind, c.mountLabel, conf)
59
+		if c.fail {
60
+			if err == nil {
61
+				t.Fatalf("Expected error, was nil, for spec %s\n", c.bind)
62
+			}
63
+			continue
64
+		}
65
+
66
+		if m.Destination != c.expDest {
67
+			t.Fatalf("Expected destination %s, was %s, for spec %s\n", c.expDest, m.Destination, c.bind)
68
+		}
69
+
70
+		if m.Source != c.expSource {
71
+			t.Fatalf("Expected source %s, was %s, for spec %s\n", c.expSource, m.Source, c.bind)
72
+		}
73
+
74
+		if m.Name != c.expName {
75
+			t.Fatalf("Expected name %s, was %s for spec %s\n", c.expName, m.Name, c.bind)
76
+		}
77
+
78
+		if m.Driver != c.expDriver {
79
+			t.Fatalf("Expected driver %s, was %s, for spec %s\n", c.expDriver, m.Driver, c.bind)
80
+		}
81
+
82
+		if m.RW != c.expRW {
83
+			t.Fatalf("Expected RW %v, was %v for spec %s\n", c.expRW, m.RW, c.bind)
84
+		}
85
+	}
86
+}
0 87
deleted file mode 100644
... ...
@@ -1,23 +0,0 @@
1
-// +build !experimental
2
-
3
-package daemon
4
-
5
-import (
6
-	"fmt"
7
-	"path/filepath"
8
-
9
-	"github.com/docker/docker/volume"
10
-	"github.com/docker/docker/volume/drivers"
11
-)
12
-
13
-func getVolumeDriver(_ string) (volume.Driver, error) {
14
-	return volumedrivers.Lookup(volume.DefaultDriverName)
15
-}
16
-
17
-func parseVolumeSource(spec string) (string, string, error) {
18
-	if !filepath.IsAbs(spec) {
19
-		return "", "", fmt.Errorf("cannot bind mount volume: %s volume paths must be absolute.", spec)
20
-	}
21
-
22
-	return "", spec, nil
23
-}
24 1
deleted file mode 100644
... ...
@@ -1,82 +0,0 @@
1
-// +build !experimental
2
-
3
-package daemon
4
-
5
-import (
6
-	"io/ioutil"
7
-	"os"
8
-	"testing"
9
-
10
-	"github.com/docker/docker/runconfig"
11
-	"github.com/docker/docker/volume"
12
-	"github.com/docker/docker/volume/drivers"
13
-	"github.com/docker/docker/volume/local"
14
-)
15
-
16
-func TestGetVolumeDefaultDriver(t *testing.T) {
17
-	tmp, err := ioutil.TempDir("", "volume-test-")
18
-	if err != nil {
19
-		t.Fatal(err)
20
-	}
21
-	defer os.RemoveAll(tmp)
22
-
23
-	l, err := local.New(tmp)
24
-	if err != nil {
25
-		t.Fatal(err)
26
-	}
27
-	volumedrivers.Register(l, volume.DefaultDriverName)
28
-	d, err := getVolumeDriver("missing")
29
-	if err != nil {
30
-		t.Fatal(err)
31
-	}
32
-
33
-	if d.Name() != volume.DefaultDriverName {
34
-		t.Fatalf("Expected local driver, was %s\n", d.Name)
35
-	}
36
-}
37
-
38
-func TestParseBindMount(t *testing.T) {
39
-	cases := []struct {
40
-		bind       string
41
-		expDest    string
42
-		expSource  string
43
-		expName    string
44
-		mountLabel string
45
-		expRW      bool
46
-		fail       bool
47
-	}{
48
-		{"/tmp:/tmp", "/tmp", "/tmp", "", "", true, false},
49
-		{"/tmp:/tmp:ro", "/tmp", "/tmp", "", "", false, false},
50
-		{"/tmp:/tmp:rw", "/tmp", "/tmp", "", "", true, false},
51
-		{"/tmp:/tmp:foo", "/tmp", "/tmp", "", "", false, true},
52
-		{"name:/tmp", "", "", "", "", false, true},
53
-		{"local/name:/tmp:rw", "", "", "", "", true, true},
54
-	}
55
-
56
-	for _, c := range cases {
57
-		conf := &runconfig.Config{}
58
-		m, err := parseBindMount(c.bind, c.mountLabel, conf)
59
-		if c.fail {
60
-			if err == nil {
61
-				t.Fatalf("Expected error, was nil, for spec %s\n", c.bind)
62
-			}
63
-			continue
64
-		}
65
-
66
-		if m.Destination != c.expDest {
67
-			t.Fatalf("Expected destination %s, was %s, for spec %s\n", c.expDest, m.Destination, c.bind)
68
-		}
69
-
70
-		if m.Source != c.expSource {
71
-			t.Fatalf("Expected source %s, was %s, for spec %s\n", c.expSource, m.Source, c.bind)
72
-		}
73
-
74
-		if m.Name != c.expName {
75
-			t.Fatalf("Expected name %s, was %s for spec %s\n", c.expName, m.Name, c.bind)
76
-		}
77
-
78
-		if m.RW != c.expRW {
79
-			t.Fatalf("Expected RW %v, was %v for spec %s\n", c.expRW, m.RW, c.bind)
80
-		}
81
-	}
82
-}
83 1
new file mode 100644
... ...
@@ -0,0 +1,22 @@
0
+<!--[metadata]>
1
+title = "Extend Docker"
2
+description = "How to extend Docker with plugins"
3
+keywords = ["extend, plugins, docker, documentation, developer"]
4
+[menu.main]
5
+identifier = "mn_extend"
6
+name = "Extend Docker"
7
+weight = 6
8
+<![end-metadata]-->
9
+
10
+
11
+## Extending Docker
12
+
13
+Currently, you can extend Docker by adding a plugin. This section contains the following topics:
14
+
15
+* [Understand Docker plugins](plugins.md)
16
+* [Write a volume plugin](plugins_volumes.md)
17
+* [Docker plugin API](plugin_api.md)
18
+
19
+ 
0 20
\ No newline at end of file
1 21
new file mode 100644
... ...
@@ -0,0 +1,135 @@
0
+<!--[metadata]>
1
+title = "Plugins API"
2
+description = "How to write Docker plugins extensions "
3
+keywords = ["API, Usage, plugins, documentation, developer"]
4
+[menu.main]
5
+parent = "mn_extend"
6
+weight=1
7
+<![end-metadata]-->
8
+
9
+# Docker Plugin API
10
+
11
+Docker plugins are out-of-process extensions which add capabilities to the
12
+Docker Engine.
13
+
14
+This page is intended for people who want to develop their own Docker plugin.
15
+If you just want to learn about or use Docker plugins, look
16
+[here](plugins.md).
17
+
18
+## What plugins are
19
+
20
+A plugin is a process running on the same docker host as the docker daemon,
21
+which registers itself by placing a file in one of the plugin directories described in [Plugin discovery](#plugin-discovery).
22
+
23
+Plugins have human-readable names, which are short, lowercase strings. For
24
+example, `flocker` or `weave`.
25
+
26
+Plugins can run inside or outside containers. Currently running them outside
27
+containers is recommended.
28
+
29
+## Plugin discovery
30
+
31
+Docker discovers plugins by looking for them in the plugin directory whenever a
32
+user or container tries to use one by name.
33
+
34
+There are three types of files which can be put in the plugin directory.
35
+
36
+* `.sock` files are UNIX domain sockets.
37
+* `.spec` files are text files containing a URL, such as `unix:///other.sock`.
38
+* `.json` files are text files containing a full json specification for the plugin.
39
+
40
+UNIX domain socket files must be located under `/run/docker/plugins`, whereas
41
+spec files can be located either under `/etc/docker/plugins` or `/usr/lib/docker/plugins`.
42
+
43
+The name of the file (excluding the extension) determines the plugin name.
44
+
45
+For example, the `flocker` plugin might create a UNIX socket at
46
+`/run/docker/plugins/flocker.sock`.
47
+
48
+You can define each plugin into a separated subdirectory if you want to isolate definitions from each other.
49
+For example, you can create the `flocker` socket under `/run/docker/plugins/flocker/flocker.sock` and only
50
+mount `/run/docker/plugins/flocker` inside the `flocker` container.
51
+
52
+Docker always searches for unix sockets in `/run/docker/plugins` first. It checks for spec or json files under
53
+`/etc/docker/plugins` and `/usr/lib/docker/plugins` if the socket doesn't exist. The directory scan stops as
54
+soon as it finds the first plugin definition with the given name.
55
+
56
+### JSON specification
57
+
58
+This is the JSON format for a plugin:
59
+
60
+```json
61
+{
62
+  "Name": "plugin-example",
63
+  "Addr": "https://example.com/docker/plugin",
64
+  "TLSConfig": {
65
+    "InsecureSkipVerify": false,
66
+    "CAFile": "/usr/shared/docker/certs/example-ca.pem",
67
+    "CertFile": "/usr/shared/docker/certs/example-cert.pem",
68
+    "KeyFile": "/usr/shared/docker/certs/example-key.pem",
69
+  }
70
+}
71
+```
72
+
73
+The `TLSConfig` field is optional and TLS will only be verified if this configuration is present.
74
+
75
+## Plugin lifecycle
76
+
77
+Plugins should be started before Docker, and stopped after Docker.  For
78
+example, when packaging a plugin for a platform which supports `systemd`, you
79
+might use [`systemd` dependencies](
80
+http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before=) to
81
+manage startup and shutdown order.
82
+
83
+When upgrading a plugin, you should first stop the Docker daemon, upgrade the
84
+plugin, then start Docker again.
85
+
86
+## Plugin activation
87
+
88
+When a plugin is first referred to -- either by a user referring to it by name
89
+(e.g.  `docker run --volume-driver=foo`) or a container already configured to
90
+use a plugin being started -- Docker looks for the named plugin in the plugin
91
+directory and activates it with a handshake. See Handshake API below.
92
+
93
+Plugins are *not* activated automatically at Docker daemon startup. Rather,
94
+they are activated only lazily, or on-demand, when they are needed.
95
+
96
+## API design
97
+
98
+The Plugin API is RPC-style JSON over HTTP, much like webhooks.
99
+
100
+Requests flow *from* the Docker daemon *to* the plugin.  So the plugin needs to
101
+implement an HTTP server and bind this to the UNIX socket mentioned in the
102
+"plugin discovery" section.
103
+
104
+All requests are HTTP `POST` requests.
105
+
106
+The API is versioned via an Accept header, which currently is always set to
107
+`application/vnd.docker.plugins.v1+json`.
108
+
109
+## Handshake API
110
+
111
+Plugins are activated via the following "handshake" API call.
112
+
113
+### /Plugin.Activate
114
+
115
+**Request:** empty body
116
+
117
+**Response:**
118
+```
119
+{
120
+    "Implements": ["VolumeDriver"]
121
+}
122
+```
123
+
124
+Responds with a list of Docker subsystems which this plugin implements.
125
+After activation, the plugin will then be sent events from this subsystem.
126
+
127
+## Plugin retries
128
+
129
+Attempts to call a method on a plugin are retried with an exponential backoff
130
+for up to 30 seconds. This may help when packaging plugins as containers, since
131
+it gives plugin containers a chance to start up before failing any user
132
+containers which depend on them.
0 133
new file mode 100644
... ...
@@ -0,0 +1,55 @@
0
+<!--[metadata]>
1
+title = "Extending Docker with plugins"
2
+description = "How to add additional functionality to Docker with plugins extensions"
3
+keywords = ["Examples, Usage, plugins, docker, documentation, user guide"]
4
+[menu.main]
5
+parent = "mn_extend"
6
+weight=-1
7
+<![end-metadata]-->
8
+
9
+# Understand Docker plugins
10
+
11
+You can extend the capabilities of the Docker Engine by loading third-party
12
+plugins.
13
+
14
+## Types of plugins
15
+
16
+Plugins extend Docker's functionality.  They come in specific types.  For
17
+example, a [volume plugin](plugins_volume.md) might enable Docker
18
+volumes to persist across multiple Docker hosts.
19
+
20
+Currently Docker supports volume and network driver plugins. In the future it
21
+will support additional plugin types.
22
+
23
+## Installing a plugin
24
+
25
+Follow the instructions in the plugin's documentation.
26
+
27
+## Finding a plugin
28
+
29
+The following plugins exist:
30
+
31
+* The [Flocker plugin](https://clusterhq.com/docker-plugin/) is a volume plugin
32
+  which provides multi-host portable volumes for Docker, enabling you to run
33
+  databases and other stateful containers and move them around across a cluster
34
+  of machines.
35
+
36
+* The [GlusterFS plugin](https://github.com/calavera/docker-volume-glusterfs) is
37
+  another volume plugin that provides multi-host volumes management for Docker
38
+  using GlusterFS.
39
+
40
+* The [Keywhiz plugin](https://github.com/calavera/docker-volume-keywhiz) is
41
+  a plugin that provides credentials and secret management using Keywhiz as
42
+  a central repository.
43
+
44
+## Troubleshooting a plugin
45
+
46
+If you are having problems with Docker after loading a plugin, ask the authors
47
+of the plugin for help. The Docker team may not be able to assist you.
48
+
49
+## Writing a plugin
50
+
51
+If you are interested in writing a plugin for Docker, or seeing how they work
52
+under the hood, see the [docker plugins reference](plugin_api.md).
0 53
new file mode 100644
... ...
@@ -0,0 +1,158 @@
0
+<!--[metadata]>
1
+title = "Volume plugins"
2
+description = "How to manage data with external volume plugins"
3
+keywords = ["Examples, Usage, volume, docker, data, volumes, plugin, api"]
4
+[menu.main]
5
+parent = "mn_extend"
6
+<![end-metadata]-->
7
+
8
+# Write a volume plugin
9
+
10
+Docker volume plugins enable Docker deployments to be integrated with external
11
+storage systems, such as Amazon EBS, and enable data volumes to persist beyond
12
+the lifetime of a single Docker host. See the [plugin documentation](plugins.md)
13
+for more information.
14
+
15
+# Command-line changes
16
+
17
+A volume plugin makes use of the `-v`and `--volume-driver` flag on the `docker run` command.  The `-v` flag accepts a volume name and the `--volume-driver` flag a driver type, for example: 
18
+
19
+    $ docker run -ti -v volumename:/data --volume-driver=flocker   busybox sh
20
+
21
+This command passes the `volumename` through to the volume plugin as a
22
+user-given name for the volume. The `volumename` must not begin with a `/`. 
23
+
24
+By having the user specify a  `volumename`, a plugin can associate the volume
25
+with an external volume beyond the lifetime of a single container or container
26
+host. This can be used, for example, to move a stateful container from one
27
+server to another.
28
+
29
+By specifying a `volumedriver` in conjunction with a `volumename`, users can use plugins such as [Flocker](https://clusterhq.com/docker-plugin/) to manage volumes external to a single host, such as those on EBS. 
30
+
31
+
32
+# Create a VolumeDriver
33
+
34
+The container creation endpoint (`/containers/create`) accepts a `VolumeDriver`
35
+field of type `string` allowing to specify the name of the driver. It's default
36
+value of `"local"` (the default driver for local volumes).
37
+
38
+# Volume plugin protocol
39
+
40
+If a plugin registers itself as a `VolumeDriver` when activated, then it is
41
+expected to provide writeable paths on the host filesystem for the Docker
42
+daemon to provide to containers to consume.
43
+
44
+The Docker daemon handles bind-mounting the provided paths into user
45
+containers.
46
+
47
+### /VolumeDriver.Create
48
+
49
+**Request**:
50
+```
51
+{
52
+    "Name": "volume_name"
53
+}
54
+```
55
+
56
+Instruct the plugin that the user wants to create a volume, given a user
57
+specified volume name.  The plugin does not need to actually manifest the
58
+volume on the filesystem yet (until Mount is called).
59
+
60
+**Response**:
61
+```
62
+{
63
+    "Err": null
64
+}
65
+```
66
+
67
+Respond with a string error if an error occurred.
68
+
69
+### /VolumeDriver.Remove
70
+
71
+**Request**:
72
+```
73
+{
74
+    "Name": "volume_name"
75
+}
76
+```
77
+
78
+Create a volume, given a user specified volume name.
79
+
80
+**Response**:
81
+```
82
+{
83
+    "Err": null
84
+}
85
+```
86
+
87
+Respond with a string error if an error occurred.
88
+
89
+### /VolumeDriver.Mount
90
+
91
+**Request**:
92
+```
93
+{
94
+    "Name": "volume_name"
95
+}
96
+```
97
+
98
+Docker requires the plugin to provide a volume, given a user specified volume
99
+name. This is called once per container start.
100
+
101
+**Response**:
102
+```
103
+{
104
+    "Mountpoint": "/path/to/directory/on/host",
105
+    "Err": null
106
+}
107
+```
108
+
109
+Respond with the path on the host filesystem where the volume has been made
110
+available, and/or a string error if an error occurred.
111
+
112
+### /VolumeDriver.Path
113
+
114
+**Request**:
115
+```
116
+{
117
+    "Name": "volume_name"
118
+}
119
+```
120
+
121
+Docker needs reminding of the path to the volume on the host.
122
+
123
+**Response**:
124
+```
125
+{
126
+    "Mountpoint": "/path/to/directory/on/host",
127
+    "Err": null
128
+}
129
+```
130
+
131
+Respond with the path on the host filesystem where the volume has been made
132
+available, and/or a string error if an error occurred.
133
+
134
+### /VolumeDriver.Unmount
135
+
136
+**Request**:
137
+```
138
+{
139
+    "Name": "volume_name"
140
+}
141
+```
142
+
143
+Indication that Docker no longer is using the named volume. This is called once
144
+per container stop.  Plugin may deduce that it is safe to deprovision it at
145
+this point.
146
+
147
+**Response**:
148
+```
149
+{
150
+    "Err": null
151
+}
152
+```
153
+
154
+Respond with a string error if an error occurred.
155
+
0 156
deleted file mode 100644
... ...
@@ -1,130 +0,0 @@
1
-# Experimental: Docker Plugin API
2
-
3
-Docker plugins are out-of-process extensions which add capabilities to the
4
-Docker Engine.
5
-
6
-This page is intended for people who want to develop their own Docker plugin.
7
-If you just want to learn about or use Docker plugins, look
8
-[here](/experimental/plugins.md).
9
-
10
-This is an experimental feature. For information on installing and using experimental features, see [the experimental feature overview](README.md).
11
-
12
-## What plugins are
13
-
14
-A plugin is a process running on the same docker host as the docker daemon,
15
-which registers itself by placing a file in one of the plugin directories described in [Plugin discovery](#plugin-discovery).
16
-
17
-Plugins have human-readable names, which are short, lowercase strings. For
18
-example, `flocker` or `weave`.
19
-
20
-Plugins can run inside or outside containers. Currently running them outside
21
-containers is recommended.
22
-
23
-## Plugin discovery
24
-
25
-Docker discovers plugins by looking for them in the plugin directory whenever a
26
-user or container tries to use one by name.
27
-
28
-There are three types of files which can be put in the plugin directory.
29
-
30
-* `.sock` files are UNIX domain sockets.
31
-* `.spec` files are text files containing a URL, such as `unix:///other.sock`.
32
-* `.json` files are text files containing a full json specification for the plugin.
33
-
34
-UNIX domain socket files must be located under `/run/docker/plugins`, whereas
35
-spec files can be located either under `/etc/docker/plugins` or `/usr/lib/docker/plugins`.
36
-
37
-The name of the file (excluding the extension) determines the plugin name.
38
-
39
-For example, the `flocker` plugin might create a UNIX socket at
40
-`/run/docker/plugins/flocker.sock`.
41
-
42
-You can define each plugin into a separated subdirectory if you want to isolate definitions from each other.
43
-For example, you can create the `flocker` socket under `/run/docker/plugins/flocker/flocker.sock` and only
44
-mount `/run/docker/plugins/flocker` inside the `flocker` container.
45
-
46
-Docker always searches for unix sockets in `/run/docker/plugins` first. It checks for spec or json files under
47
-`/etc/docker/plugins` and `/usr/lib/docker/plugins` if the socket doesn't exist. The directory scan stops as
48
-soon as it finds the first plugin definition with the given name.
49
-
50
-### JSON specification
51
-
52
-This is the JSON format for a plugin:
53
-
54
-```json
55
-{
56
-  "Name": "plugin-example",
57
-  "Addr": "https://example.com/docker/plugin",
58
-  "TLSConfig": {
59
-    "InsecureSkipVerify": false,
60
-    "CAFile": "/usr/shared/docker/certs/example-ca.pem",
61
-    "CertFile": "/usr/shared/docker/certs/example-cert.pem",
62
-    "KeyFile": "/usr/shared/docker/certs/example-key.pem",
63
-  }
64
-}
65
-```
66
-
67
-The `TLSConfig` field is optional and TLS will only be verified if this configuration is present.
68
-
69
-## Plugin lifecycle
70
-
71
-Plugins should be started before Docker, and stopped after Docker.  For
72
-example, when packaging a plugin for a platform which supports `systemd`, you
73
-might use [`systemd` dependencies](
74
-http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before=) to
75
-manage startup and shutdown order.
76
-
77
-When upgrading a plugin, you should first stop the Docker daemon, upgrade the
78
-plugin, then start Docker again.
79
-
80
-If a plugin is packaged as a container, this may cause issues. Plugins as
81
-containers are currently considered experimental due to these shutdown/startup
82
-ordering issues. These issues are mitigated by plugin retries (see below).
83
-
84
-## Plugin activation
85
-
86
-When a plugin is first referred to -- either by a user referring to it by name
87
-(e.g.  `docker run --volume-driver=foo`) or a container already configured to
88
-use a plugin being started -- Docker looks for the named plugin in the plugin
89
-directory and activates it with a handshake. See Handshake API below.
90
-
91
-Plugins are *not* activated automatically at Docker daemon startup. Rather,
92
-they are activated only lazily, or on-demand, when they are needed.
93
-
94
-## API design
95
-
96
-The Plugin API is RPC-style JSON over HTTP, much like webhooks.
97
-
98
-Requests flow *from* the Docker daemon *to* the plugin.  So the plugin needs to
99
-implement an HTTP server and bind this to the UNIX socket mentioned in the
100
-"plugin discovery" section.
101
-
102
-All requests are HTTP `POST` requests.
103
-
104
-The API is versioned via an Accept header, which currently is always set to
105
-`application/vnd.docker.plugins.v1+json`.
106
-
107
-## Handshake API
108
-
109
-Plugins are activated via the following "handshake" API call.
110
-
111
-### /Plugin.Activate
112
-
113
-**Request:** empty body
114
-
115
-**Response:**
116
-```
117
-{
118
-    "Implements": ["VolumeDriver"]
119
-}
120
-```
121
-
122
-Responds with a list of Docker subsystems which this plugin implements.
123
-After activation, the plugin will then be sent events from this subsystem.
124
-
125
-## Plugin retries
126
-
127
-Attempts to call a method on a plugin are retried with an exponential backoff
128
-for up to 30 seconds. This may help when packaging plugins as containers, since
129
-it gives plugin containers a chance to start up before failing any user
130
-containers which depend on them.
131 1
deleted file mode 100644
... ...
@@ -1,52 +0,0 @@
1
-# Experimental: Extend Docker with a plugin 
2
-
3
-You can extend the capabilities of the Docker Engine by loading third-party
4
-plugins. 
5
-
6
-This is an experimental feature. For information on installing and using experimental features, see [the experimental feature overview](README.md).
7
-
8
-## Types of plugins
9
-
10
-Plugins extend Docker's functionality.  They come in specific types.  For
11
-example, a [volume plugin](/experimental/plugins_volume.md) might enable Docker
12
-volumes to persist across multiple Docker hosts.
13
-
14
-Currently Docker supports volume and network driver plugins. In the future it
15
-will support additional plugin types.
16
-
17
-## Installing a plugin
18
-
19
-Follow the instructions in the plugin's documentation.
20
-
21
-## Finding a plugin
22
-
23
-The following plugins exist:
24
-
25
-* The [Flocker plugin](https://clusterhq.com/docker-plugin/) is a volume plugin
26
-  which provides multi-host portable volumes for Docker, enabling you to run
27
-  databases and other stateful containers and move them around across a cluster
28
-  of machines.
29
-
30
-* The [Weave plugin](https://github.com/weaveworks/docker-plugin) is a network
31
-  driver plugin which provides a virtual, multi-host network for containers.
32
-
33
-* The [Calico plugin](https://github.com/metaswitch/calico-docker) is a network
34
-  driver plugin which provides a multi-host network for containers with routes 
35
-  distributed by BGP.
36
-
37
-## Troubleshooting a plugin
38
-
39
-If you are having problems with Docker after loading a plugin, ask the authors
40
-of the plugin for help. The Docker team may not be able to assist you.
41
-
42
-## Writing a plugin
43
-
44
-If you are interested in writing a plugin for Docker, or seeing how they work
45
-under the hood, see the [docker plugins reference](/experimental/plugin_api.md).
46
-
47
-# Related GitHub PRs and issues
48
-
49
-- [#13222](https://github.com/docker/docker/pull/13222) Plugins plumbing
50
-
51
-Send us feedback and comments on [#13419](https://github.com/docker/docker/issues/13419),
52
-or on the usual Google Groups (docker-user, docker-dev) and IRC channels.
... ...
@@ -18,7 +18,7 @@ commands. For example,
18 18
 
19 19
     docker network create -d weave mynet
20 20
 
21
-Some network driver plugins are listed in [plugins.md](plugins.md)
21
+Some network driver plugins are listed in [plugins.md](/docs/extend/plugins.md)
22 22
 
23 23
 The network thus created is owned by the plugin, so subsequent commands
24 24
 referring to that network will also be run through the plugin.
25 25
deleted file mode 100644
... ...
@@ -1,160 +0,0 @@
1
-# Experimental: Docker volume plugins
2
-
3
-Docker volume plugins enable Docker deployments to be integrated with external
4
-storage systems, such as Amazon EBS, and enable data volumes to persist beyond
5
-the lifetime of a single Docker host. See the [plugin documentation](/experimental/plugins.md)
6
-for more information.
7
-
8
-This is an experimental feature. For information on installing and using experimental features, see [the experimental feature overview](README.md).
9
-
10
-# Command-line changes
11
-
12
-This experimental feature introduces two changes to the `docker run` command:
13
-
14
-- The `--volume-driver` flag is introduced.
15
-- The `-v` syntax is changed to accept a volume name a first component.
16
-
17
-Example:
18
-
19
-    $ docker run -ti -v volumename:/data --volume-driver=flocker busybox sh
20
-
21
-By specifying a volume name in conjunction with a volume driver, volume plugins
22
-such as [Flocker](https://clusterhq.com/docker-plugin/), once installed, can be
23
-used to manage volumes external to a single host, such as those on EBS. In this
24
-example, "volumename" is passed through to the volume plugin as a user-given
25
-name for the volume which allows the plugin to associate it with an external
26
-volume beyond the lifetime of a single container or container host. This can be
27
-used, for example, to move a stateful container from one server to another.
28
-
29
-The `volumename` must not begin with a `/`.
30
-
31
-# API changes
32
-
33
-The container creation endpoint (`/containers/create`) accepts a `VolumeDriver`
34
-field of type `string` allowing to specify the name of the driver. It's default
35
-value of `"local"` (the default driver for local volumes).
36
-
37
-# Volume plugin protocol
38
-
39
-If a plugin registers itself as a `VolumeDriver` when activated, then it is
40
-expected to provide writeable paths on the host filesystem for the Docker
41
-daemon to provide to containers to consume.
42
-
43
-The Docker daemon handles bind-mounting the provided paths into user
44
-containers.
45
-
46
-### /VolumeDriver.Create
47
-
48
-**Request**:
49
-```
50
-{
51
-    "Name": "volume_name"
52
-}
53
-```
54
-
55
-Instruct the plugin that the user wants to create a volume, given a user
56
-specified volume name.  The plugin does not need to actually manifest the
57
-volume on the filesystem yet (until Mount is called).
58
-
59
-**Response**:
60
-```
61
-{
62
-    "Err": null
63
-}
64
-```
65
-
66
-Respond with a string error if an error occurred.
67
-
68
-### /VolumeDriver.Remove
69
-
70
-**Request**:
71
-```
72
-{
73
-    "Name": "volume_name"
74
-}
75
-```
76
-
77
-Create a volume, given a user specified volume name.
78
-
79
-**Response**:
80
-```
81
-{
82
-    "Err": null
83
-}
84
-```
85
-
86
-Respond with a string error if an error occurred.
87
-
88
-### /VolumeDriver.Mount
89
-
90
-**Request**:
91
-```
92
-{
93
-    "Name": "volume_name"
94
-}
95
-```
96
-
97
-Docker requires the plugin to provide a volume, given a user specified volume
98
-name. This is called once per container start.
99
-
100
-**Response**:
101
-```
102
-{
103
-    "Mountpoint": "/path/to/directory/on/host",
104
-    "Err": null
105
-}
106
-```
107
-
108
-Respond with the path on the host filesystem where the volume has been made
109
-available, and/or a string error if an error occurred.
110
-
111
-### /VolumeDriver.Path
112
-
113
-**Request**:
114
-```
115
-{
116
-    "Name": "volume_name"
117
-}
118
-```
119
-
120
-Docker needs reminding of the path to the volume on the host.
121
-
122
-**Response**:
123
-```
124
-{
125
-    "Mountpoint": "/path/to/directory/on/host",
126
-    "Err": null
127
-}
128
-```
129
-
130
-Respond with the path on the host filesystem where the volume has been made
131
-available, and/or a string error if an error occurred.
132
-
133
-### /VolumeDriver.Unmount
134
-
135
-**Request**:
136
-```
137
-{
138
-    "Name": "volume_name"
139
-}
140
-```
141
-
142
-Indication that Docker no longer is using the named volume. This is called once
143
-per container stop.  Plugin may deduce that it is safe to deprovision it at
144
-this point.
145
-
146
-**Response**:
147
-```
148
-{
149
-    "Err": null
150
-}
151
-```
152
-
153
-Respond with a string error if an error occurred.
154
-
155
-# Related GitHub PRs and issues
156
-
157
-- [#13161](https://github.com/docker/docker/pull/13161) Volume refactor and external volume plugins
158
-
159
-Send us feedback and comments on [#13420](https://github.com/docker/docker/issues/13420),
160
-or on the usual Google Groups (docker-user, docker-dev) and IRC channels.
... ...
@@ -1,4 +1,3 @@
1
-// +build experimental
2 1
 // +build !windows
3 2
 
4 3
 package main
... ...
@@ -26,15 +26,16 @@ var (
26 26
 
27 27
 // validateNM is the set of fields passed to validateNetMode()
28 28
 type validateNM struct {
29
-	netMode      NetworkMode
30
-	flHostname   *string
31
-	flLinks      opts.ListOpts
32
-	flDns        opts.ListOpts
33
-	flExtraHosts opts.ListOpts
34
-	flMacAddress *string
35
-	flPublish    opts.ListOpts
36
-	flPublishAll *bool
37
-	flExpose     opts.ListOpts
29
+	netMode        NetworkMode
30
+	flHostname     *string
31
+	flLinks        opts.ListOpts
32
+	flDns          opts.ListOpts
33
+	flExtraHosts   opts.ListOpts
34
+	flMacAddress   *string
35
+	flPublish      opts.ListOpts
36
+	flPublishAll   *bool
37
+	flExpose       opts.ListOpts
38
+	flVolumeDriver string
38 39
 }
39 40
 
40 41
 func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSet, error) {
... ...
@@ -94,6 +95,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
94 94
 		flReadonlyRootfs  = cmd.Bool([]string{"-read-only"}, false, "Mount the container's root filesystem as read only")
95 95
 		flLoggingDriver   = cmd.String([]string{"-log-driver"}, "", "Logging driver for container")
96 96
 		flCgroupParent    = cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
97
+		flVolumeDriver    = cmd.String([]string{"-volume-driver"}, "", "Optional volume driver for the container")
97 98
 	)
98 99
 
99 100
 	cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR")
... ...
@@ -332,6 +334,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
332 332
 		Entrypoint:      entrypoint,
333 333
 		WorkingDir:      *flWorkingDir,
334 334
 		Labels:          convertKVStringsToMap(labels),
335
+		VolumeDriver:    *flVolumeDriver,
335 336
 	}
336 337
 
337 338
 	hostConfig := &HostConfig{
... ...
@@ -10,12 +10,10 @@ type experimentalFlags struct {
10 10
 
11 11
 func attachExperimentalFlags(cmd *flag.FlagSet) *experimentalFlags {
12 12
 	flags := make(map[string]interface{})
13
-	flags["volume-driver"] = cmd.String([]string{"-volume-driver"}, "", "Optional volume driver for the container")
14 13
 	flags["publish-service"] = cmd.String([]string{"-publish-service"}, "", "Publish this container as a service")
15 14
 	return &experimentalFlags{flags: flags}
16 15
 }
17 16
 
18 17
 func applyExperimentalFlags(exp *experimentalFlags, config *Config, hostConfig *HostConfig) {
19
-	config.VolumeDriver = *(exp.flags["volume-driver"]).(*string)
20 18
 	config.PublishService = *(exp.flags["publish-service"]).(*string)
21 19
 }