Remove volume stubs and use the experimental path as the only path.
Signed-off-by: David Calavera <david.calavera@gmail.com>
| ... | ... |
@@ -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. |
| ... | ... |
@@ -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 |
} |