diff --git a/daemon/volumes.go b/daemon/volumes.go index cca28225ca..71a3ed941f 100644 --- a/daemon/volumes.go +++ b/daemon/volumes.go @@ -12,6 +12,7 @@ import ( "github.com/docker/docker/pkg/system" "github.com/docker/docker/runconfig" "github.com/docker/docker/volume" + "github.com/docker/docker/volume/drivers" "github.com/docker/docker/volume/local" "github.com/opencontainers/runc/libcontainer/label" ) @@ -333,3 +334,18 @@ func removeVolume(v volume.Volume) error { } return vd.Remove(v) } + +func getVolumeDriver(name string) (volume.Driver, error) { + if name == "" { + name = volume.DefaultDriverName + } + return volumedrivers.Lookup(name) +} + +func parseVolumeSource(spec string) (string, string, error) { + if !filepath.IsAbs(spec) { + return spec, "", nil + } + + return "", spec, nil +} diff --git a/daemon/volumes_experimental.go b/daemon/volumes_experimental.go deleted file mode 100644 index 7ff37901a7..0000000000 --- a/daemon/volumes_experimental.go +++ /dev/null @@ -1,25 +0,0 @@ -// +build experimental - -package daemon - -import ( - "path/filepath" - - "github.com/docker/docker/volume" - "github.com/docker/docker/volume/drivers" -) - -func getVolumeDriver(name string) (volume.Driver, error) { - if name == "" { - name = volume.DefaultDriverName - } - return volumedrivers.Lookup(name) -} - -func parseVolumeSource(spec string) (string, string, error) { - if !filepath.IsAbs(spec) { - return spec, "", nil - } - - return "", spec, nil -} diff --git a/daemon/volumes_experimental_unit_test.go b/daemon/volumes_linux_unit_test.go similarity index 100% rename from daemon/volumes_experimental_unit_test.go rename to daemon/volumes_linux_unit_test.go diff --git a/daemon/volumes_stubs.go b/daemon/volumes_stubs.go deleted file mode 100644 index 6061811400..0000000000 --- a/daemon/volumes_stubs.go +++ /dev/null @@ -1,23 +0,0 @@ -// +build !experimental - -package daemon - -import ( - "fmt" - "path/filepath" - - "github.com/docker/docker/volume" - "github.com/docker/docker/volume/drivers" -) - -func getVolumeDriver(_ string) (volume.Driver, error) { - return volumedrivers.Lookup(volume.DefaultDriverName) -} - -func parseVolumeSource(spec string) (string, string, error) { - if !filepath.IsAbs(spec) { - return "", "", fmt.Errorf("cannot bind mount volume: %s volume paths must be absolute.", spec) - } - - return "", spec, nil -} diff --git a/daemon/volumes_stubs_unit_test.go b/daemon/volumes_stubs_unit_test.go deleted file mode 100644 index 5f100e5263..0000000000 --- a/daemon/volumes_stubs_unit_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// +build !experimental - -package daemon - -import ( - "io/ioutil" - "os" - "testing" - - "github.com/docker/docker/runconfig" - "github.com/docker/docker/volume" - "github.com/docker/docker/volume/drivers" - "github.com/docker/docker/volume/local" -) - -func TestGetVolumeDefaultDriver(t *testing.T) { - tmp, err := ioutil.TempDir("", "volume-test-") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - l, err := local.New(tmp) - if err != nil { - t.Fatal(err) - } - volumedrivers.Register(l, volume.DefaultDriverName) - d, err := getVolumeDriver("missing") - if err != nil { - t.Fatal(err) - } - - if d.Name() != volume.DefaultDriverName { - t.Fatalf("Expected local driver, was %s\n", d.Name) - } -} - -func TestParseBindMount(t *testing.T) { - cases := []struct { - bind string - expDest string - expSource string - expName string - mountLabel string - expRW bool - fail bool - }{ - {"/tmp:/tmp", "/tmp", "/tmp", "", "", true, false}, - {"/tmp:/tmp:ro", "/tmp", "/tmp", "", "", false, false}, - {"/tmp:/tmp:rw", "/tmp", "/tmp", "", "", true, false}, - {"/tmp:/tmp:foo", "/tmp", "/tmp", "", "", false, true}, - {"name:/tmp", "", "", "", "", false, true}, - {"local/name:/tmp:rw", "", "", "", "", true, true}, - } - - for _, c := range cases { - conf := &runconfig.Config{} - m, err := parseBindMount(c.bind, c.mountLabel, conf) - if c.fail { - if err == nil { - t.Fatalf("Expected error, was nil, for spec %s\n", c.bind) - } - continue - } - - if m.Destination != c.expDest { - t.Fatalf("Expected destination %s, was %s, for spec %s\n", c.expDest, m.Destination, c.bind) - } - - if m.Source != c.expSource { - t.Fatalf("Expected source %s, was %s, for spec %s\n", c.expSource, m.Source, c.bind) - } - - if m.Name != c.expName { - t.Fatalf("Expected name %s, was %s for spec %s\n", c.expName, m.Name, c.bind) - } - - if m.RW != c.expRW { - t.Fatalf("Expected RW %v, was %v for spec %s\n", c.expRW, m.RW, c.bind) - } - } -} diff --git a/docs/extend/index.md b/docs/extend/index.md new file mode 100644 index 0000000000..61f8689de5 --- /dev/null +++ b/docs/extend/index.md @@ -0,0 +1,22 @@ + + + +## Extending Docker + +Currently, you can extend Docker by adding a plugin. This section contains the following topics: + +* [Understand Docker plugins](plugins.md) +* [Write a volume plugin](plugins_volumes.md) +* [Docker plugin API](plugin_api.md) + + \ No newline at end of file diff --git a/experimental/plugin_api.md b/docs/extend/plugin_api.md similarity index 90% rename from experimental/plugin_api.md rename to docs/extend/plugin_api.md index 8820c15708..8e2862f6cb 100644 --- a/experimental/plugin_api.md +++ b/docs/extend/plugin_api.md @@ -1,13 +1,22 @@ -# Experimental: Docker Plugin API + + +# Docker Plugin API Docker plugins are out-of-process extensions which add capabilities to the Docker Engine. This page is intended for people who want to develop their own Docker plugin. If you just want to learn about or use Docker plugins, look -[here](/experimental/plugins.md). - -This is an experimental feature. For information on installing and using experimental features, see [the experimental feature overview](README.md). +[here](plugins.md). ## What plugins are @@ -77,10 +86,6 @@ manage startup and shutdown order. When upgrading a plugin, you should first stop the Docker daemon, upgrade the plugin, then start Docker again. -If a plugin is packaged as a container, this may cause issues. Plugins as -containers are currently considered experimental due to these shutdown/startup -ordering issues. These issues are mitigated by plugin retries (see below). - ## Plugin activation When a plugin is first referred to -- either by a user referring to it by name diff --git a/experimental/plugins.md b/docs/extend/plugins.md similarity index 50% rename from experimental/plugins.md rename to docs/extend/plugins.md index 15de7b767d..1578579b1c 100644 --- a/experimental/plugins.md +++ b/docs/extend/plugins.md @@ -1,14 +1,23 @@ -# Experimental: Extend Docker with a plugin + + +# Understand Docker plugins You can extend the capabilities of the Docker Engine by loading third-party -plugins. - -This is an experimental feature. For information on installing and using experimental features, see [the experimental feature overview](README.md). +plugins. ## Types of plugins Plugins extend Docker's functionality. They come in specific types. For -example, a [volume plugin](/experimental/plugins_volume.md) might enable Docker +example, a [volume plugin](plugins_volume.md) might enable Docker volumes to persist across multiple Docker hosts. Currently Docker supports volume and network driver plugins. In the future it @@ -27,12 +36,13 @@ The following plugins exist: databases and other stateful containers and move them around across a cluster of machines. -* The [Weave plugin](https://github.com/weaveworks/docker-plugin) is a network - driver plugin which provides a virtual, multi-host network for containers. +* The [GlusterFS plugin](https://github.com/calavera/docker-volume-glusterfs) is + another volume plugin that provides multi-host volumes management for Docker + using GlusterFS. -* The [Calico plugin](https://github.com/metaswitch/calico-docker) is a network - driver plugin which provides a multi-host network for containers with routes - distributed by BGP. +* The [Keywhiz plugin](https://github.com/calavera/docker-volume-keywhiz) is + a plugin that provides credentials and secret management using Keywhiz as + a central repository. ## Troubleshooting a plugin @@ -42,11 +52,4 @@ of the plugin for help. The Docker team may not be able to assist you. ## Writing a plugin If you are interested in writing a plugin for Docker, or seeing how they work -under the hood, see the [docker plugins reference](/experimental/plugin_api.md). - -# Related GitHub PRs and issues - -- [#13222](https://github.com/docker/docker/pull/13222) Plugins plumbing - -Send us feedback and comments on [#13419](https://github.com/docker/docker/issues/13419), -or on the usual Google Groups (docker-user, docker-dev) and IRC channels. +under the hood, see the [docker plugins reference](plugin_api.md). diff --git a/experimental/plugins_volume.md b/docs/extend/plugins_volume.md similarity index 65% rename from experimental/plugins_volume.md rename to docs/extend/plugins_volume.md index ebc5491a8c..e9dc1ebaa2 100644 --- a/experimental/plugins_volume.md +++ b/docs/extend/plugins_volume.md @@ -1,34 +1,38 @@ -# Experimental: Docker volume plugins + + +# Write a volume plugin Docker volume plugins enable Docker deployments to be integrated with external storage systems, such as Amazon EBS, and enable data volumes to persist beyond -the lifetime of a single Docker host. See the [plugin documentation](/experimental/plugins.md) +the lifetime of a single Docker host. See the [plugin documentation](plugins.md) for more information. -This is an experimental feature. For information on installing and using experimental features, see [the experimental feature overview](README.md). - # Command-line changes -This experimental feature introduces two changes to the `docker run` command: +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: -- The `--volume-driver` flag is introduced. -- The `-v` syntax is changed to accept a volume name a first component. + $ docker run -ti -v volumename:/data --volume-driver=flocker busybox sh -Example: +This command passes the `volumename` through to the volume plugin as a +user-given name for the volume. The `volumename` must not begin with a `/`. - $ docker run -ti -v volumename:/data --volume-driver=flocker busybox sh +By having the user specify a `volumename`, a plugin can associate the volume +with an external volume beyond the lifetime of a single container or container +host. This can be used, for example, to move a stateful container from one +server to another. -By specifying a volume name in conjunction with a volume driver, volume plugins -such as [Flocker](https://clusterhq.com/docker-plugin/), once installed, can be -used to manage volumes external to a single host, such as those on EBS. In this -example, "volumename" is passed through to the volume plugin as a user-given -name for the volume which allows the plugin to associate it with an external -volume beyond the lifetime of a single container or container host. This can be -used, for example, to move a stateful container from one server to another. +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. -The `volumename` must not begin with a `/`. -# API changes +# Create a VolumeDriver The container creation endpoint (`/containers/create`) accepts a `VolumeDriver` field of type `string` allowing to specify the name of the driver. It's default @@ -152,9 +156,3 @@ this point. Respond with a string error if an error occurred. -# Related GitHub PRs and issues - -- [#13161](https://github.com/docker/docker/pull/13161) Volume refactor and external volume plugins - -Send us feedback and comments on [#13420](https://github.com/docker/docker/issues/13420), -or on the usual Google Groups (docker-user, docker-dev) and IRC channels. diff --git a/experimental/plugins_network.md b/experimental/plugins_network.md index 755537ef80..0902bee475 100644 --- a/experimental/plugins_network.md +++ b/experimental/plugins_network.md @@ -18,7 +18,7 @@ commands. For example, docker network create -d weave mynet -Some network driver plugins are listed in [plugins.md](plugins.md) +Some network driver plugins are listed in [plugins.md](/docs/extend/plugins.md) The network thus created is owned by the plugin, so subsequent commands referring to that network will also be run through the plugin. diff --git a/integration-cli/docker_cli_start_volume_driver_unix_test.go b/integration-cli/docker_cli_start_volume_driver_unix_test.go index ddf8d228b4..71f9f2d540 100644 --- a/integration-cli/docker_cli_start_volume_driver_unix_test.go +++ b/integration-cli/docker_cli_start_volume_driver_unix_test.go @@ -1,4 +1,3 @@ -// +build experimental // +build !windows package main diff --git a/runconfig/parse.go b/runconfig/parse.go index b04398e5ff..96c27ad9c7 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -26,15 +26,16 @@ var ( // validateNM is the set of fields passed to validateNetMode() type validateNM struct { - netMode NetworkMode - flHostname *string - flLinks opts.ListOpts - flDns opts.ListOpts - flExtraHosts opts.ListOpts - flMacAddress *string - flPublish opts.ListOpts - flPublishAll *bool - flExpose opts.ListOpts + netMode NetworkMode + flHostname *string + flLinks opts.ListOpts + flDns opts.ListOpts + flExtraHosts opts.ListOpts + flMacAddress *string + flPublish opts.ListOpts + flPublishAll *bool + flExpose opts.ListOpts + flVolumeDriver string } 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 flReadonlyRootfs = cmd.Bool([]string{"-read-only"}, false, "Mount the container's root filesystem as read only") flLoggingDriver = cmd.String([]string{"-log-driver"}, "", "Logging driver for container") flCgroupParent = cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container") + flVolumeDriver = cmd.String([]string{"-volume-driver"}, "", "Optional volume driver for the container") ) 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 Entrypoint: entrypoint, WorkingDir: *flWorkingDir, Labels: convertKVStringsToMap(labels), + VolumeDriver: *flVolumeDriver, } hostConfig := &HostConfig{ diff --git a/runconfig/parse_experimental.go b/runconfig/parse_experimental.go index d00e69c5c6..8f8612ba55 100644 --- a/runconfig/parse_experimental.go +++ b/runconfig/parse_experimental.go @@ -10,12 +10,10 @@ type experimentalFlags struct { func attachExperimentalFlags(cmd *flag.FlagSet) *experimentalFlags { flags := make(map[string]interface{}) - flags["volume-driver"] = cmd.String([]string{"-volume-driver"}, "", "Optional volume driver for the container") flags["publish-service"] = cmd.String([]string{"-publish-service"}, "", "Publish this container as a service") return &experimentalFlags{flags: flags} } func applyExperimentalFlags(exp *experimentalFlags, config *Config, hostConfig *HostConfig) { - config.VolumeDriver = *(exp.flags["volume-driver"]).(*string) config.PublishService = *(exp.flags["publish-service"]).(*string) }