1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #20195 from tiborvass/1.10.1-cherrypicks

1.10.1 cherrypicks
This commit is contained in:
Arnaud Porterie 2016-02-10 11:36:28 -08:00
commit 6ab0256048
36 changed files with 571 additions and 127 deletions

View file

@ -5,6 +5,44 @@ information on the list of deprecated flags and APIs please have a look at
https://docs.docker.com/misc/deprecated/ where target removal dates can also
be found.
## 1.10.1 (2016-02-11)
### Runtime
* Do not stop daemon on migration hard failure [#20156](https://github.com/docker/docker/pull/20156)
- Fix various issues with migration to content-addressable images [#20058](https://github.com/docker/docker/pull/20058)
- Fix ZFS permission bug with user namespaces [#20045](https://github.com/docker/docker/pull/20045)
- Do not leak /dev/mqueue from the host to all containers, keep it container-specific [#19876](https://github.com/docker/docker/pull/19876) [#20133](https://github.com/docker/docker/pull/20133)
- Fix `docker ps --filter before=...` to work without needing `-a` flag [#20135](https://github.com/docker/docker/pull/20135)
### Security
- Fix issue preventing docker events to work properly with authorization plugin [#20002](https://github.com/docker/docker/pull/20002)
### Distribution
* Add additional verifications and prevent from uploading invalid data to registries [#20164](https://github.com/docker/docker/pull/20164)
- Fix regression preventing uppercase characters in image reference hostname [#20175](https://github.com/docker/docker/pull/20175)
### Networking
- Fix embedded DNS for user-defined networks in the presence of firewalld [#20060](https://github.com/docker/docker/pull/20060)
- Fix issue where removing a network during shutdown left Docker inoperable [#20181](https://github.com/docker/docker/issues/20181)
- Embedded DNS is now able to return compressed results [#20181](https://github.com/docker/docker/issues/20181)
- Fix port-mapping issue with `userland-proxy=false` [#20181](https://github.com/docker/docker/issues/20181)
### Logging
- Fix bug where tcp+tls protocol would be rejected [#20109](https://github.com/docker/docker/pull/20109)
### Volumes
- Fix issue whereby older volume drivers would not receive volume options [#19983](https://github.com/docker/docker/pull/19983)
### Misc
- Remove TasksMax from Docker systemd service [#20167](https://github.com/docker/docker/pull/20167)
## 1.10.0 (2016-02-04)
**IMPORTANT**: Docker 1.10 uses a new content-addressable storage for images and layers.

View file

@ -44,7 +44,6 @@ type Container struct {
HostnamePath string
HostsPath string
ShmPath string
MqueuePath string
ResolvConfPath string
SeccompProfile string
}
@ -563,18 +562,6 @@ func (container *Container) UnmountIpcMounts(unmount func(pth string) error) {
}
}
if !container.HasMountFor("/dev/mqueue") {
mqueuePath, err := container.MqueueResourcePath()
if err != nil {
logrus.Error(err)
warnings = append(warnings, err.Error())
} else if mqueuePath != "" {
if err := unmount(mqueuePath); err != nil {
warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", mqueuePath, err))
}
}
}
if len(warnings) > 0 {
logrus.Warnf("failed to cleanup ipc mounts:\n%v", strings.Join(warnings, "\n"))
}
@ -593,16 +580,6 @@ func (container *Container) IpcMounts() []execdriver.Mount {
Propagation: volume.DefaultPropagationMode,
})
}
if !container.HasMountFor("/dev/mqueue") {
label.SetFileLabel(container.MqueuePath, container.MountLabel)
mounts = append(mounts, execdriver.Mount{
Source: container.MqueuePath,
Destination: "/dev/mqueue",
Writable: true,
Propagation: volume.DefaultPropagationMode,
})
}
return mounts
}

View file

@ -1758,6 +1758,11 @@ _docker_run() {
__docker_nospace
fi
;;
seccomp:*)
local cur=${cur##*:}
_filedir
COMPREPLY+=( $( compgen -W "unconfined" -- "$cur" ) )
;;
*)
COMPREPLY=( $( compgen -W "label apparmor seccomp" -S ":" -- "$cur") )
__docker_nospace

View file

@ -11,7 +11,6 @@ MountFlags=slave
LimitNOFILE=1048576
LimitNPROC=1048576
LimitCORE=infinity
TasksMax=1048576
TimeoutStartSec=0
[Install]

View file

@ -93,11 +93,6 @@ func (daemon *Daemon) populateCommand(c *container.Container, env []string) erro
return err
}
c.MqueuePath, err = c.MqueueResourcePath()
if err != nil {
return err
}
if c.HostConfig.IpcMode.IsContainer() {
ic, err := daemon.getIpcContainer(c)
if err != nil {
@ -105,18 +100,13 @@ func (daemon *Daemon) populateCommand(c *container.Container, env []string) erro
}
ipc.ContainerID = ic.ID
c.ShmPath = ic.ShmPath
c.MqueuePath = ic.MqueuePath
} else {
ipc.HostIpc = c.HostConfig.IpcMode.IsHost()
if ipc.HostIpc {
if _, err := os.Stat("/dev/shm"); err != nil {
return fmt.Errorf("/dev/shm is not mounted, but must be for --ipc=host")
}
if _, err := os.Stat("/dev/mqueue"); err != nil {
return fmt.Errorf("/dev/mqueue is not mounted, but must be for --ipc=host")
}
c.ShmPath = "/dev/shm"
c.MqueuePath = "/dev/mqueue"
}
}
@ -1057,21 +1047,6 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
}
}
if !c.HasMountFor("/dev/mqueue") {
mqueuePath, err := c.MqueueResourcePath()
if err != nil {
return err
}
if err := idtools.MkdirAllAs(mqueuePath, 0700, rootUID, rootGID); err != nil {
return err
}
if err := syscall.Mount("mqueue", mqueuePath, "mqueue", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), ""); err != nil {
return fmt.Errorf("mounting mqueue mqueue : %s", err)
}
}
return nil
}

View file

@ -64,6 +64,12 @@ func New() *configs.Config {
Flags: syscall.MS_NOSUID | syscall.MS_NOEXEC,
Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
},
{
Source: "mqueue",
Destination: "/dev/mqueue",
Device: "mqueue",
Flags: defaultMountFlags,
},
{
Source: "sysfs",
Destination: "/sys",

View file

@ -308,10 +308,14 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
return "", err
}
err = mount.Mount(filesystem, mountpoint, "zfs", options)
if err != nil {
if err := mount.Mount(filesystem, mountpoint, "zfs", options); err != nil {
return "", fmt.Errorf("error creating zfs mount of %s to %s: %v", filesystem, mountpoint, err)
}
// this could be our first mount after creation of the filesystem, and the root dir may still have root
// permissions instead of the remapped root uid:gid (if user namespaces are enabled):
if err := os.Chown(mountpoint, rootUID, rootGID); err != nil {
return "", fmt.Errorf("error modifying zfs mountpoint (%s) directory ownership: %v", mountpoint, err)
}
return mountpoint, nil
}

View file

@ -74,6 +74,13 @@ type listContext struct {
filters filters.Args
// exitAllowed is a list of exit codes allowed to filter with
exitAllowed []int
// FIXME Remove this for 1.12 as --since and --before are deprecated
// beforeContainer is a filter to ignore containers that appear before the one given
beforeContainer *container.Container
// sinceContainer is a filter to stop the filtering when the iterator arrive to the given container
sinceContainer *container.Container
// beforeFilter is a filter to ignore containers that appear before the one given
// this is used for --filter=before= and --before=, the latter is deprecated.
beforeFilter *container.Container
@ -165,6 +172,9 @@ func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error)
}
var beforeContFilter, sinceContFilter *container.Container
// FIXME remove this for 1.12 as --since and --before are deprecated
var beforeContainer, sinceContainer *container.Container
err = psFilters.WalkValues("before", func(value string) error {
beforeContFilter, err = daemon.GetContainer(value)
return err
@ -201,15 +211,17 @@ func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error)
})
}
if config.Before != "" && beforeContFilter == nil {
beforeContFilter, err = daemon.GetContainer(config.Before)
// FIXME remove this for 1.12 as --since and --before are deprecated
if config.Before != "" {
beforeContainer, err = daemon.GetContainer(config.Before)
if err != nil {
return nil, err
}
}
if config.Since != "" && sinceContFilter == nil {
sinceContFilter, err = daemon.GetContainer(config.Since)
// FIXME remove this for 1.12 as --since and --before are deprecated
if config.Since != "" {
sinceContainer, err = daemon.GetContainer(config.Since)
if err != nil {
return nil, err
}
@ -220,6 +232,8 @@ func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error)
ancestorFilter: ancestorFilter,
images: imagesFilter,
exitAllowed: filtExited,
beforeContainer: beforeContainer,
sinceContainer: sinceContainer,
beforeFilter: beforeContFilter,
sinceFilter: sinceContFilter,
ContainersConfig: config,
@ -231,7 +245,8 @@ func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error)
// It also decides if the iteration should be stopped or not.
func includeContainerInList(container *container.Container, ctx *listContext) iterationAction {
// Do not include container if it's stopped and we're not filters
if !container.Running && !ctx.All && ctx.Limit <= 0 && ctx.beforeFilter == nil && ctx.sinceFilter == nil {
// FIXME remove the ctx.beforContainer part of the condition for 1.12 as --since and --before are deprecated
if !container.Running && !ctx.All && ctx.Limit <= 0 && ctx.beforeContainer == nil && ctx.sinceContainer == nil {
return excludeContainer
}
@ -255,6 +270,21 @@ func includeContainerInList(container *container.Container, ctx *listContext) it
return excludeContainer
}
// FIXME remove this for 1.12 as --since and --before are deprecated
if ctx.beforeContainer != nil {
if container.ID == ctx.beforeContainer.ID {
ctx.beforeContainer = nil
}
return excludeContainer
}
// FIXME remove this for 1.12 as --since and --before are deprecated
if ctx.sinceContainer != nil {
if container.ID == ctx.sinceContainer.ID {
return stopIteration
}
}
// Do not include container if it's in the list before the filter container.
// Set the filter container to nil to include the rest of containers after this one.
if ctx.beforeFilter != nil {

View file

@ -367,7 +367,7 @@ do_install() {
# aufs is preferred over devicemapper; try to ensure the driver is available.
if ! grep -q aufs /proc/filesystems && ! $sh_c 'modprobe aufs'; then
if uname -r | grep -q -- '-generic' && dpkg -l 'linux-image-*-generic' | grep -q '^ii' 2>/dev/null; then
if uname -r | grep -q -- '-generic' && dpkg -l 'linux-image-*-generic' | grep -qE '^ii|^hi' 2>/dev/null; then
kern_extras="linux-image-extra-$(uname -r) linux-image-extra-virtual"
apt_get_update

View file

@ -27,7 +27,7 @@ clone git github.com/RackSec/srslog 6eb773f331e46fbba8eecb8e794e635e75fc04de
clone git github.com/imdario/mergo 0.2.1
#get libnetwork packages
clone git github.com/docker/libnetwork v0.6.0-rc7
clone git github.com/docker/libnetwork v0.6.1-rc2
clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4

View file

@ -231,6 +231,9 @@ func (is *store) SetParent(id, parent ID) error {
if parentMeta == nil {
return fmt.Errorf("unknown parent image ID %s", parent.String())
}
if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil {
delete(is.images[parent].children, id)
}
parentMeta.children[id] = struct{}{}
return is.fs.SetMetadata(id, "parent", []byte(parent))
}

View file

@ -233,6 +233,62 @@ func TestSearchAfterDelete(t *testing.T) {
}
}
func TestParentReset(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "images-fs-store")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
fs, err := NewFSStoreBackend(tmpdir)
if err != nil {
t.Fatal(err)
}
is, err := NewImageStore(fs, &mockLayerGetReleaser{})
if err != nil {
t.Fatal(err)
}
id, err := is.Create([]byte(`{"comment": "abc1", "rootfs": {"type": "layers"}}`))
if err != nil {
t.Fatal(err)
}
id2, err := is.Create([]byte(`{"comment": "abc2", "rootfs": {"type": "layers"}}`))
if err != nil {
t.Fatal(err)
}
id3, err := is.Create([]byte(`{"comment": "abc3", "rootfs": {"type": "layers"}}`))
if err != nil {
t.Fatal(err)
}
if err := is.SetParent(id, id2); err != nil {
t.Fatal(err)
}
ids := is.Children(id2)
if actual, expected := len(ids), 1; expected != actual {
t.Fatalf("wrong number of children: %d, got %d", expected, actual)
}
if err := is.SetParent(id, id3); err != nil {
t.Fatal(err)
}
ids = is.Children(id2)
if actual, expected := len(ids), 0; expected != actual {
t.Fatalf("wrong number of children after parent reset: %d, got %d", expected, actual)
}
ids = is.Children(id3)
if actual, expected := len(ids), 1; expected != actual {
t.Fatalf("wrong number of children after parent reset: %d, got %d", expected, actual)
}
}
type mockLayerGetReleaser struct{}
func (ls *mockLayerGetReleaser) Get(layer.ChainID) (layer.Layer, error) {

View file

@ -11,10 +11,15 @@ import (
"os"
"strings"
"bufio"
"bytes"
"github.com/docker/docker/pkg/authorization"
"github.com/docker/docker/pkg/integration/checker"
"github.com/docker/docker/pkg/plugins"
"github.com/go-check/check"
"os/exec"
"strconv"
"time"
)
const (
@ -221,6 +226,71 @@ func (s *DockerAuthzSuite) TestAuthZPluginDenyResponse(c *check.C) {
c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: authorization denied by plugin %s: %s\n", testAuthZPlugin, unauthorizedMessage))
}
// TestAuthZPluginAllowEventStream verifies event stream propogates correctly after request pass through by the authorization plugin
func (s *DockerAuthzSuite) TestAuthZPluginAllowEventStream(c *check.C) {
testRequires(c, DaemonIsLinux)
// Start the authorization plugin
err := s.d.Start("--authorization-plugin=" + testAuthZPlugin)
c.Assert(err, check.IsNil)
s.ctrl.reqRes.Allow = true
s.ctrl.resRes.Allow = true
startTime := strconv.FormatInt(daemonTime(c).Unix(), 10)
// Add another command to to enable event pipelining
eventsCmd := exec.Command(s.d.cmd.Path, "--host", s.d.sock(), "events", "--since", startTime)
stdout, err := eventsCmd.StdoutPipe()
if err != nil {
c.Assert(err, check.IsNil)
}
observer := eventObserver{
buffer: new(bytes.Buffer),
command: eventsCmd,
scanner: bufio.NewScanner(stdout),
startTime: startTime,
}
err = observer.Start()
c.Assert(err, checker.IsNil)
defer observer.Stop()
// Create a container and wait for the creation events
_, err = s.d.Cmd("pull", "busybox")
c.Assert(err, check.IsNil)
out, err := s.d.Cmd("run", "-d", "busybox", "top")
c.Assert(err, check.IsNil)
containerID := strings.TrimSpace(out)
events := map[string]chan bool{
"create": make(chan bool),
"start": make(chan bool),
}
matcher := matchEventLine(containerID, "container", events)
processor := processEventMatch(events)
go observer.Match(matcher, processor)
// Ensure all events are received
for event, eventChannel := range events {
select {
case <-time.After(5 * time.Second):
// Fail the test
observer.CheckEventError(c, containerID, event, matcher)
c.FailNow()
case <-eventChannel:
// Ignore, event received
}
}
// Ensure both events and container endpoints are passed to the authorization plugin
assertURIRecorded(c, s.ctrl.requestsURIs, "/events")
assertURIRecorded(c, s.ctrl.requestsURIs, "/containers/create")
assertURIRecorded(c, s.ctrl.requestsURIs, fmt.Sprintf("/containers/%s/start", containerID))
}
func (s *DockerAuthzSuite) TestAuthZPluginErrorResponse(c *check.C) {
err := s.d.Start("--authorization-plugin=" + testAuthZPlugin)
c.Assert(err, check.IsNil)

View file

@ -178,7 +178,8 @@ func (s *DockerSuite) TestBuildCancellationKillsSleep(c *check.C) {
}
matcher := matchEventLine(buildID, "container", testActions)
go observer.Match(matcher)
processor := processEventMatch(testActions)
go observer.Match(matcher, processor)
select {
case <-time.After(10 * time.Second):

View file

@ -224,7 +224,8 @@ func (s *DockerSuite) TestEventsStreaming(c *check.C) {
}
matcher := matchEventLine(containerID, "container", testActions)
go observer.Match(matcher)
processor := processEventMatch(testActions)
go observer.Match(matcher, processor)
select {
case <-time.After(5 * time.Second):
@ -280,7 +281,8 @@ func (s *DockerSuite) TestEventsImageUntagDelete(c *check.C) {
}
matcher := matchEventLine(imageID, "image", testActions)
go observer.Match(matcher)
processor := processEventMatch(testActions)
go observer.Match(matcher, processor)
select {
case <-time.After(10 * time.Second):

View file

@ -48,8 +48,6 @@ func (s *DockerSuite) TestPsListContainersBase(c *check.C) {
out, _ = dockerCmd(c, "ps")
c.Assert(assertContainerList(out, []string{fourthID, secondID, firstID}), checker.Equals, true, check.Commentf("RUNNING: Container list is not in the correct order: \n%s", out))
// from here all flag '-a' is ignored
// limit
out, _ = dockerCmd(c, "ps", "-n=2", "-a")
expected := []string{fourthID, thirdID}
@ -61,56 +59,138 @@ func (s *DockerSuite) TestPsListContainersBase(c *check.C) {
// filter since
out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-a")
expected = []string{fourthID, thirdID, secondID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE & ALL: Container list is not in the correct order: \n%s", out))
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE filter & ALL: Container list is not in the correct order: \n%s", out))
out, _ = dockerCmd(c, "ps", "-f", "since="+firstID)
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE: Container list is not in the correct order: \n%s", out))
expected = []string{fourthID, secondID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE filter: Container list is not in the correct order: \n%s", out))
// filter before
out, _ = dockerCmd(c, "ps", "-f", "before="+thirdID, "-a")
expected = []string{secondID, firstID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("BEFORE & ALL: Container list is not in the correct order: \n%s", out))
out, _ = dockerCmd(c, "ps", "-f", "before="+fourthID, "-a")
expected = []string{thirdID, secondID, firstID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("BEFORE filter & ALL: Container list is not in the correct order: \n%s", out))
out, _ = dockerCmd(c, "ps", "-f", "before="+thirdID)
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("BEFORE: Container list is not in the correct order: \n%s", out))
out, _ = dockerCmd(c, "ps", "-f", "before="+fourthID)
expected = []string{secondID, firstID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("BEFORE filter: Container list is not in the correct order: \n%s", out))
// filter since & before
out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-f", "before="+fourthID, "-a")
expected = []string{thirdID, secondID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE, BEFORE & ALL: Container list is not in the correct order: \n%s", out))
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE filter, BEFORE filter & ALL: Container list is not in the correct order: \n%s", out))
out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-f", "before="+fourthID)
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE, BEFORE: Container list is not in the correct order: \n%s", out))
expected = []string{secondID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE filter, BEFORE filter: Container list is not in the correct order: \n%s", out))
// filter since & limit
out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-n=2", "-a")
expected = []string{fourthID, thirdID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE filter, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-n=2")
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE, LIMIT: Container list is not in the correct order: \n%s", out))
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE filter, LIMIT: Container list is not in the correct order: \n%s", out))
// filter before & limit
out, _ = dockerCmd(c, "ps", "-f", "before="+fourthID, "-n=1", "-a")
expected = []string{thirdID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("BEFORE, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("BEFORE filter, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
out, _ = dockerCmd(c, "ps", "-f", "before="+fourthID, "-n=1")
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("BEFORE, LIMIT: Container list is not in the correct order: \n%s", out))
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("BEFORE filter, LIMIT: Container list is not in the correct order: \n%s", out))
// filter since & filter before & limit
out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-f", "before="+fourthID, "-n=1", "-a")
expected = []string{thirdID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE, BEFORE, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE filter, BEFORE filter, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-f", "before="+fourthID, "-n=1")
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE, BEFORE, LIMIT: Container list is not in the correct order: \n%s", out))
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE filter, BEFORE filter, LIMIT: Container list is not in the correct order: \n%s", out))
}
// FIXME remove this for 1.12 as --since and --before are deprecated
func (s *DockerSuite) TestPsListContainersDeprecatedSinceAndBefore(c *check.C) {
out, _ := runSleepingContainer(c, "-d")
firstID := strings.TrimSpace(out)
out, _ = runSleepingContainer(c, "-d")
secondID := strings.TrimSpace(out)
// not long running
out, _ = dockerCmd(c, "run", "-d", "busybox", "true")
thirdID := strings.TrimSpace(out)
out, _ = runSleepingContainer(c, "-d")
fourthID := strings.TrimSpace(out)
// make sure the second is running
c.Assert(waitRun(secondID), checker.IsNil)
// make sure third one is not running
dockerCmd(c, "wait", thirdID)
// make sure the forth is running
c.Assert(waitRun(fourthID), checker.IsNil)
// since
out, _ = dockerCmd(c, "ps", "--since="+firstID, "-a")
expected := []string{fourthID, thirdID, secondID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE & ALL: Container list is not in the correct order: %v \n%s", expected, out))
out, _ = dockerCmd(c, "ps", "--since="+firstID)
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE: Container list is not in the correct order: %v \n%s", expected, out))
// before
out, _ = dockerCmd(c, "ps", "--before="+thirdID, "-a")
expected = []string{secondID, firstID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("BEFORE & ALL: Container list is not in the correct order: %v \n%s", expected, out))
out, _ = dockerCmd(c, "ps", "--before="+thirdID)
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("BEFORE: Container list is not in the correct order: %v \n%s", expected, out))
// since & before
out, _ = dockerCmd(c, "ps", "--since="+firstID, "--before="+fourthID, "-a")
expected = []string{thirdID, secondID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE, BEFORE & ALL: Container list is not in the correct order: %v \n%s", expected, out))
out, _ = dockerCmd(c, "ps", "--since="+firstID, "--before="+fourthID)
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE, BEFORE: Container list is not in the correct order: %v \n%s", expected, out))
// since & limit
out, _ = dockerCmd(c, "ps", "--since="+firstID, "-n=2", "-a")
expected = []string{fourthID, thirdID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE, LIMIT & ALL: Container list is not in the correct order: %v \n%s", expected, out))
out, _ = dockerCmd(c, "ps", "--since="+firstID, "-n=2")
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE, LIMIT: Container list is not in the correct order: %v \n%s", expected, out))
// before & limit
out, _ = dockerCmd(c, "ps", "--before="+fourthID, "-n=1", "-a")
expected = []string{thirdID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("BEFORE, LIMIT & ALL: Container list is not in the correct order: %v \n%s", expected, out))
out, _ = dockerCmd(c, "ps", "--before="+fourthID, "-n=1")
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("BEFORE, LIMIT: Container list is not in the correct order: %v \n%s", expected, out))
// since & before & limit
out, _ = dockerCmd(c, "ps", "--since="+firstID, "--before="+fourthID, "-n=1", "-a")
expected = []string{thirdID}
c.Assert(assertContainerList(out, expected), checker.Equals, true, check.Commentf("SINCE, BEFORE, LIMIT & ALL: Container list is not in the correct order: %v \n%s", expected, out))
}
func assertContainerList(out string, expected []string) bool {
lines := strings.Split(strings.Trim(out, "\n "), "\n")
// FIXME remove this for 1.12 as --since and --before are deprecated
// This is here to remove potential Warning: lines (printed out with deprecated flags)
for i := 0; i < 2; i++ {
if strings.Contains(lines[0], "Warning:") {
lines = lines[1:]
}
}
if len(lines)-1 != len(expected) {
return false
}

View file

@ -2365,7 +2365,7 @@ func (s *DockerSuite) TestRunModeIpcContainer(c *check.C) {
// Not applicable on Windows as uses Unix-specific capabilities
testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
out, _ := dockerCmd(c, "run", "-d", "busybox", "sh", "-c", "echo -n test > /dev/shm/test && top")
out, _ := dockerCmd(c, "run", "-d", "busybox", "sh", "-c", "echo -n test > /dev/shm/test && touch /dev/mqueue/toto && top")
id := strings.TrimSpace(out)
state, err := inspectField(id, "State.Running")
@ -2391,6 +2391,18 @@ func (s *DockerSuite) TestRunModeIpcContainer(c *check.C) {
if catOutput != "test" {
c.Fatalf("Output of /dev/shm/test expected test but found: %s", catOutput)
}
// check that /dev/mqueue is actually of mqueue type
grepOutput, _ := dockerCmd(c, "run", fmt.Sprintf("--ipc=container:%s", id), "busybox", "grep", "/dev/mqueue", "/proc/mounts")
if !strings.HasPrefix(grepOutput, "mqueue /dev/mqueue mqueue rw") {
c.Fatalf("Output of 'grep /proc/mounts' expected 'mqueue /dev/mqueue mqueue rw' but found: %s", grepOutput)
}
lsOutput, _ := dockerCmd(c, "run", fmt.Sprintf("--ipc=container:%s", id), "busybox", "ls", "/dev/mqueue")
lsOutput = strings.Trim(lsOutput, "\n")
if lsOutput != "toto" {
c.Fatalf("Output of 'ls /dev/mqueue' expected 'toto' but found: %s", lsOutput)
}
}
func (s *DockerSuite) TestRunModeIpcContainerNotExists(c *check.C) {
@ -2417,9 +2429,11 @@ func (s *DockerSuite) TestRunModeIpcContainerNotRunning(c *check.C) {
func (s *DockerSuite) TestRunMountShmMqueueFromHost(c *check.C) {
// Not applicable on Windows as uses Unix-specific capabilities
testRequires(c, SameHostDaemon, DaemonIsLinux)
testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
dockerCmd(c, "run", "-d", "--name", "shmfromhost", "-v", "/dev/shm:/dev/shm", "busybox", "sh", "-c", "echo -n test > /dev/shm/test && top")
dockerCmd(c, "run", "-d", "--name", "shmfromhost", "-v", "/dev/shm:/dev/shm", "-v", "/dev/mqueue:/dev/mqueue", "busybox", "sh", "-c", "echo -n test > /dev/shm/test && touch /dev/mqueue/toto && top")
defer os.Remove("/dev/mqueue/toto")
defer os.Remove("/dev/shm/test")
volPath, err := inspectMountSourceField("shmfromhost", "/dev/shm")
c.Assert(err, check.IsNil)
if volPath != "/dev/shm" {
@ -2430,6 +2444,11 @@ func (s *DockerSuite) TestRunMountShmMqueueFromHost(c *check.C) {
if out != "test" {
c.Fatalf("Output of /dev/shm/test expected test but found: %s", out)
}
// Check that the mq was created
if _, err := os.Stat("/dev/mqueue/toto"); err != nil {
c.Fatalf("Failed to confirm '/dev/mqueue/toto' presence on host: %s", err.Error())
}
}
func (s *DockerSuite) TestContainerNetworkMode(c *check.C) {

View file

@ -24,11 +24,18 @@ func init() {
})
}
type vol struct {
Name string
Mountpoint string
Opts map[string]string
}
type DockerExternalVolumeSuiteCompatV1_1 struct {
server *httptest.Server
ds *DockerSuite
d *Daemon
ec *eventCounter
server *httptest.Server
ds *DockerSuite
d *Daemon
ec *eventCounter
volList []vol
}
func (s *DockerExternalVolumeSuiteCompatV1_1) SetUpTest(c *check.C) {
@ -47,6 +54,7 @@ func (s *DockerExternalVolumeSuiteCompatV1_1) SetUpSuite(c *check.C) {
type pluginRequest struct {
Name string
Opts map[string]string
}
type pluginResp struct {
@ -54,12 +62,6 @@ func (s *DockerExternalVolumeSuiteCompatV1_1) SetUpSuite(c *check.C) {
Err string `json:",omitempty"`
}
type vol struct {
Name string
Mountpoint string
}
var volList []vol
read := func(b io.ReadCloser) (pluginRequest, error) {
defer b.Close()
var pr pluginRequest
@ -94,7 +96,7 @@ func (s *DockerExternalVolumeSuiteCompatV1_1) SetUpSuite(c *check.C) {
send(w, err)
return
}
volList = append(volList, vol{Name: pr.Name})
s.volList = append(s.volList, vol{Name: pr.Name, Opts: pr.Opts})
send(w, nil)
})
@ -111,13 +113,13 @@ func (s *DockerExternalVolumeSuiteCompatV1_1) SetUpSuite(c *check.C) {
return
}
for i, v := range volList {
for i, v := range s.volList {
if v.Name == pr.Name {
if err := os.RemoveAll(hostVolumePath(v.Name)); err != nil {
send(w, fmt.Sprintf(`{"Err": "%v"}`, err))
return
}
volList = append(volList[:i], volList[i+1:]...)
s.volList = append(s.volList[:i], s.volList[i+1:]...)
break
}
}
@ -213,3 +215,20 @@ func (s *DockerExternalVolumeSuiteCompatV1_1) TestExternalVolumeDriverCompatV1_1
out, err = s.d.Cmd("volume", "rm", "foo")
c.Assert(err, checker.IsNil, check.Commentf(out))
}
func (s *DockerExternalVolumeSuiteCompatV1_1) TestExternalVolumeDriverCompatOptionsV1_1(c *check.C) {
err := s.d.StartWithBusybox()
c.Assert(err, checker.IsNil)
out, err := s.d.Cmd("volume", "create", "--name", "optvol", "--driver", "test-external-volume-driver", "--opt", "opt1=opt1val", "--opt", "opt2=opt2val")
c.Assert(err, checker.IsNil, check.Commentf(out))
out, err = s.d.Cmd("volume", "inspect", "optvol")
c.Assert(err, checker.IsNil, check.Commentf(out))
c.Assert(s.volList[0].Opts["opt1"], checker.Equals, "opt1val")
c.Assert(s.volList[0].Opts["opt2"], checker.Equals, "opt2val")
out, err = s.d.Cmd("volume", "rm", "optvol")
c.Assert(err, checker.IsNil, check.Commentf(out))
}

View file

@ -1704,3 +1704,19 @@ func getInspectBody(c *check.C, version, id string) []byte {
c.Assert(status, check.Equals, http.StatusOK)
return body
}
// Run a long running idle task in a background container using the
// system-specific default image and command.
func runSleepingContainer(c *check.C, extraArgs ...string) (string, int) {
return runSleepingContainerInImage(c, defaultSleepImage, extraArgs...)
}
// Run a long running idle task in a background container using the specified
// image and the system-specific command.
func runSleepingContainerInImage(c *check.C, image string, extraArgs ...string) (string, int) {
args := []string{"run", "-d"}
args = append(args, extraArgs...)
args = append(args, image)
args = append(args, defaultSleepCommand...)
return dockerCmd(c, args...)
}

View file

@ -28,7 +28,13 @@ var (
)
// eventMatcher is a function that tries to match an event input.
type eventMatcher func(text string) bool
// It returns true if the event matches and a map with
// a set of key/value to identify the match.
type eventMatcher func(text string) (map[string]string, bool)
// eventMatchProcessor is a function to handle an event match.
// It receives a map of key/value with the information extracted in a match.
type eventMatchProcessor func(matches map[string]string)
// eventObserver runs an events commands and observes its output.
type eventObserver struct {
@ -79,13 +85,15 @@ func (e *eventObserver) Stop() {
}
// Match tries to match the events output with a given matcher.
func (e *eventObserver) Match(match eventMatcher) {
func (e *eventObserver) Match(match eventMatcher, process eventMatchProcessor) {
for e.scanner.Scan() {
text := e.scanner.Text()
e.buffer.WriteString(text)
e.buffer.WriteString("\n")
match(text)
if matches, ok := match(text); ok {
process(matches)
}
}
err := e.scanner.Err()
@ -106,7 +114,7 @@ func (e *eventObserver) CheckEventError(c *check.C, id, event string, match even
out, _ := dockerCmd(c, "events", "--since", e.startTime, "--until", until)
events := strings.Split(strings.TrimSpace(out), "\n")
for _, e := range events {
if match(e) {
if _, ok := match(e); ok {
foundEvent = true
break
}
@ -119,22 +127,30 @@ func (e *eventObserver) CheckEventError(c *check.C, id, event string, match even
}
// matchEventLine matches a text with the event regular expression.
// It returns the action and true if the regular expression matches with the given id and event type.
// It returns an empty string and false if there is no match.
// It returns the matches and true if the regular expression matches with the given id and event type.
// It returns an empty map and false if there is no match.
func matchEventLine(id, eventType string, actions map[string]chan bool) eventMatcher {
return func(text string) bool {
return func(text string) (map[string]string, bool) {
matches := parseEventText(text)
if len(matches) == 0 {
return false
return matches, false
}
if matchIDAndEventType(matches, id, eventType) {
if ch, ok := actions[matches["action"]]; ok {
close(ch)
return true
if _, ok := actions[matches["action"]]; ok {
return matches, true
}
}
return false
return matches, false
}
}
// processEventMatch closes an action channel when an event line matches the expected action.
func processEventMatch(actions map[string]chan bool) eventMatchProcessor {
return func(matches map[string]string) {
if ch, ok := actions[matches["action"]]; ok {
close(ch)
}
}
}

View file

@ -7,4 +7,10 @@ const (
isUnixCli = true
expectedFileChmod = "-rw-r--r--"
// On Unix variants, the busybox image comes with the `top` command which
// runs indefinitely while still being interruptible by a signal.
defaultSleepImage = "busybox"
)
var defaultSleepCommand = []string{"top"}

View file

@ -8,4 +8,10 @@ const (
// this is the expected file permission set on windows: gh#11395
expectedFileChmod = "-rwxr-xr-x"
// On Windows, the busybox image doesn't have the `top` command, so we rely
// on `sleep` with a high duration.
defaultSleepImage = "busybox"
)
var defaultSleepCommand = []string{"sleep", "60"}

View file

@ -2,7 +2,7 @@
% Docker Community
% JANUARY 2016
# NAME
HOME/.docker/confg.json - Default Docker configuration file
HOME/.docker/config.json - Default Docker configuration file
# INTRODUCTION

View file

@ -116,7 +116,7 @@ func (ctx *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
}
}
rm.Flush()
rm.FlushAll()
return nil
}

View file

@ -118,7 +118,7 @@ func TestResponseModifier(t *testing.T) {
m.Write([]byte("body"))
m.WriteHeader(500)
m.Flush()
m.FlushAll()
if r.Header().Get("h1") != "v1" {
t.Fatalf("Header value must exists %s", r.Header().Get("h1"))
}
@ -147,7 +147,7 @@ func TestResponseModifierOverride(t *testing.T) {
m.OverrideHeader(overrideHeaderBytes)
m.OverrideBody([]byte("override body"))
m.OverrideStatusCode(404)
m.Flush()
m.FlushAll()
if r.Header().Get("h1") != "v2" {
t.Fatalf("Header value must exists %s", r.Header().Get("h1"))
}

View file

@ -5,6 +5,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/Sirupsen/logrus"
"net"
"net/http"
)
@ -12,6 +13,8 @@ import (
// ResponseModifier allows authorization plugins to read and modify the content of the http.response
type ResponseModifier interface {
http.ResponseWriter
http.Flusher
http.CloseNotifier
// RawBody returns the current http content
RawBody() []byte
@ -32,7 +35,10 @@ type ResponseModifier interface {
OverrideStatusCode(statusCode int)
// Flush flushes all data to the HTTP response
Flush() error
FlushAll() error
// Hijacked indicates the response has been hijacked by the Docker daemon
Hijacked() bool
}
// NewResponseModifier creates a wrapper to an http.ResponseWriter to allow inspecting and modifying the content
@ -44,7 +50,10 @@ func NewResponseModifier(rw http.ResponseWriter) ResponseModifier {
// the http request/response from docker daemon
type responseModifier struct {
// The original response writer
rw http.ResponseWriter
rw http.ResponseWriter
r *http.Request
status int
// body holds the response body
body []byte
@ -52,15 +61,34 @@ type responseModifier struct {
header http.Header
// statusCode holds the response status code
statusCode int
// hijacked indicates the request has been hijacked
hijacked bool
}
func (rm *responseModifier) Hijacked() bool {
return rm.hijacked
}
// WriterHeader stores the http status code
func (rm *responseModifier) WriteHeader(s int) {
// Use original request if hijacked
if rm.hijacked {
rm.rw.WriteHeader(s)
return
}
rm.statusCode = s
}
// Header returns the internal http header
func (rm *responseModifier) Header() http.Header {
// Use original header if hijacked
if rm.hijacked {
return rm.rw.Header()
}
return rm.header
}
@ -90,6 +118,11 @@ func (rm *responseModifier) OverrideHeader(b []byte) error {
// Write stores the byte array inside content
func (rm *responseModifier) Write(b []byte) (int, error) {
if rm.hijacked {
return rm.rw.Write(b)
}
rm.body = append(rm.body, b...)
return len(b), nil
}
@ -109,6 +142,10 @@ func (rm *responseModifier) RawHeaders() ([]byte, error) {
// Hijack returns the internal connection of the wrapped http.ResponseWriter
func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
rm.hijacked = true
rm.FlushAll()
hijacker, ok := rm.rw.(http.Hijacker)
if !ok {
return nil, nil, fmt.Errorf("Internal reponse writer doesn't support the Hijacker interface")
@ -116,8 +153,30 @@ func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return hijacker.Hijack()
}
// Flush flushes all data to the HTTP response
func (rm *responseModifier) Flush() error {
// CloseNotify uses the internal close notify API of the wrapped http.ResponseWriter
func (rm *responseModifier) CloseNotify() <-chan bool {
closeNotifier, ok := rm.rw.(http.CloseNotifier)
if !ok {
logrus.Errorf("Internal reponse writer doesn't support the CloseNotifier interface")
return nil
}
return closeNotifier.CloseNotify()
}
// Flush uses the internal flush API of the wrapped http.ResponseWriter
func (rm *responseModifier) Flush() {
flusher, ok := rm.rw.(http.Flusher)
if !ok {
logrus.Errorf("Internal reponse writer doesn't support the Flusher interface")
return
}
rm.FlushAll()
flusher.Flush()
}
// FlushAll flushes all data to the HTTP response
func (rm *responseModifier) FlushAll() error {
// Copy the status code
if rm.statusCode > 0 {
rm.rw.WriteHeader(rm.statusCode)
@ -130,7 +189,15 @@ func (rm *responseModifier) Flush() error {
}
}
// Write body
_, err := rm.rw.Write(rm.body)
var err error
if len(rm.body) > 0 {
// Write body
_, err = rm.rw.Write(rm.body)
}
// Clean previous data
rm.body = nil
rm.statusCode = 0
rm.header = http.Header{}
return err
}

View file

@ -11,7 +11,7 @@ var (
validPrefixes = map[string][]string{
"url": {"http://", "https://"},
"git": {"git://", "github.com/", "git@"},
"transport": {"tcp://", "udp://", "unix://"},
"transport": {"tcp://", "tcp+tls://", "udp://", "unix://"},
}
urlPathWithFragmentSuffix = regexp.MustCompile(".git(?:#.+)?$")
)
@ -35,7 +35,7 @@ func IsGitTransport(str string) bool {
return IsURL(str) || strings.HasPrefix(str, "git://") || strings.HasPrefix(str, "git@")
}
// IsTransportURL returns true if the provided str is a transport (tcp, udp, unix) URL.
// IsTransportURL returns true if the provided str is a transport (tcp, tcp+tls, udp, unix) URL.
func IsTransportURL(str string) bool {
return checkURL(str, "transport")
}

View file

@ -18,6 +18,12 @@ var (
invalidGitUrls = []string{
"http://github.com/docker/docker.git:#branch",
}
transportUrls = []string{
"tcp://example.com",
"tcp+tls://example.com",
"udp://example.com",
"unix:///example",
}
)
func TestValidGitTransport(t *testing.T) {
@ -53,3 +59,11 @@ func TestIsGIT(t *testing.T) {
}
}
}
func TestIsTransport(t *testing.T) {
for _, url := range transportUrls {
if IsTransportURL(url) == false {
t.Fatalf("%q should be detected as valid Transport url", url)
}
}
}

View file

@ -44,7 +44,8 @@ need to package Docker your way, without denaturing it in the process.
To build Docker, you will need the following:
* A recent version of Git and Mercurial
* Go version 1.4 or later
* Go version 1.4 or later (Go version 1.5 or later required for hardware signing
support in Docker Content Trust)
* A clean checkout of the source added to a valid [Go
workspace](https://golang.org/doc/code.html#Workspaces) under the path
*src/github.com/docker/docker* (unless you plan to use `AUTO_GOPATH`,

View file

@ -1,5 +1,13 @@
# Changelog
## 0.6.1-rc2 (2016-02-09)
- Fixes https://github.com/docker/docker/issues/20132
- Fixes https://github.com/docker/docker/issues/20140
- Fixes https://github.com/docker/docker/issues/20019
## 0.6.1-rc1 (2016-02-05)
- Fixes https://github.com/docker/docker/issues/20026
## 0.6.0-rc7 (2016-02-01)
- Allow inter-network connections via exposed ports

View file

@ -115,7 +115,7 @@ func (n *bridgeNetwork) setupIPTables(config *networkConfiguration, i *bridgeInt
return iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, false)
})
n.portMapper.SetIptablesChain(filterChain, n.getNetworkBridgeName())
n.portMapper.SetIptablesChain(natChain, n.getNetworkBridgeName())
}
if err := ensureJumpRule("FORWARD", IsolationChain); err != nil {
@ -148,6 +148,9 @@ func setupIPTablesInternal(bridgeIface string, addr net.Addr, icc, ipmasq, hairp
if err := programChainRule(natRule, "NAT", enable); err != nil {
return err
}
}
if ipmasq && !hairpin {
if err := programChainRule(skipDNAT, "SKIP DNAT", enable); err != nil {
return err
}

View file

@ -325,9 +325,11 @@ func Raw(args ...string) ([]byte, error) {
if err == nil || !strings.Contains(err.Error(), "was not provided by any .service files") {
return output, err
}
}
return raw(args...)
}
func raw(args ...string) ([]byte, error) {
if err := initCheck(); err != nil {
return nil, err
}
@ -362,6 +364,15 @@ func RawCombinedOutput(args ...string) error {
return nil
}
// RawCombinedOutputNative behave as RawCombinedOutput with the difference it
// will always invoke `iptables` binary
func RawCombinedOutputNative(args ...string) error {
if output, err := raw(args...); err != nil || len(output) != 0 {
return fmt.Errorf("%s (%v)", string(output), err)
}
return nil
}
// ExistChain checks if a chain exists
func ExistChain(chain string, table Table) bool {
if _, err := Raw("-t", string(table), "-L", chain); err == nil {

View file

@ -95,7 +95,7 @@ func (r *resolver) SetupFunc() func() {
}
for _, rule := range rules {
r.err = iptables.RawCombinedOutput(rule...)
r.err = iptables.RawCombinedOutputNative(rule...)
if r.err != nil {
return
}
@ -229,6 +229,7 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
resp, _, err = c.Exchange(query, addr)
if err == nil {
resp.Compress = true
break
}
log.Errorf("external resolution failed, %s", err)

View file

@ -104,7 +104,8 @@ func (c *controller) getNetworksForScope(scope string) ([]*network, error) {
ec := &endpointCnt{n: n}
err = store.GetObject(datastore.Key(ec.Key()...), ec)
if err != nil {
return nil, fmt.Errorf("could not find endpoint count key %s for network %s while listing: %v", datastore.Key(ec.Key()...), n.Name(), err)
log.Warnf("Could not find endpoint count key %s for network %s while listing: %v", datastore.Key(ec.Key()...), n.Name(), err)
continue
}
n.epCnt = ec

View file

@ -15,7 +15,20 @@ func (a *volumeDriverAdapter) Name() string {
}
func (a *volumeDriverAdapter) Create(name string, opts map[string]string) (volume.Volume, error) {
err := a.proxy.Create(name, opts)
// First try a Get. For drivers that support Get this will return any
// existing volume.
v, err := a.proxy.Get(name)
if v != nil {
return &volumeAdapter{
proxy: a.proxy,
name: v.Name,
driverName: a.Name(),
eMount: v.Mountpoint,
}, nil
}
// Driver didn't support Get or volume didn't exist. Perform Create.
err = a.proxy.Create(name, opts)
if err != nil {
return nil, err
}

View file

@ -192,9 +192,6 @@ func (s *VolumeStore) create(name, driverName string, opts map[string]string) (v
return nil, &OpErr{Op: "create", Name: name, Err: err}
}
if v, err := vd.Get(name); err == nil {
return v, nil
}
return vd.Create(name, opts)
}