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

Fix out-of-band vol delete+create for same driver

Fix issue where out-of-band deletions and then a `docker volume create`
on the same driver caused volume to not be re-created in the driver but
return as created since it was stored in the cache.

Previous fix only worked if the driver names did not match.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
Brian Goff 2016-11-30 11:11:50 -05:00
parent dbe0752157
commit d8ce4a6e10
2 changed files with 77 additions and 40 deletions

View file

@ -570,8 +570,36 @@ func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverOutOfBandDelete(c *c
// simulate out of band volume deletion on plugin level // simulate out of band volume deletion on plugin level
delete(p.vols, "test") delete(p.vols, "test")
// test re-create with same driver
out, err = s.d.Cmd("volume", "create", "-d", driverName, "--opt", "foo=bar", "--name", "test")
c.Assert(err, checker.IsNil, check.Commentf(out))
out, err = s.d.Cmd("volume", "inspect", "test")
c.Assert(err, checker.IsNil, check.Commentf(out))
var vs []types.Volume
err = json.Unmarshal([]byte(out), &vs)
c.Assert(err, checker.IsNil)
c.Assert(vs, checker.HasLen, 1)
c.Assert(vs[0].Driver, checker.Equals, driverName)
c.Assert(vs[0].Options, checker.NotNil)
c.Assert(vs[0].Options["foo"], checker.Equals, "bar")
c.Assert(vs[0].Driver, checker.Equals, driverName)
// simulate out of band volume deletion on plugin level
delete(p.vols, "test")
// test create with different driver
out, err = s.d.Cmd("volume", "create", "-d", "local", "--name", "test") out, err = s.d.Cmd("volume", "create", "-d", "local", "--name", "test")
c.Assert(err, checker.IsNil, check.Commentf(out)) c.Assert(err, checker.IsNil, check.Commentf(out))
out, err = s.d.Cmd("volume", "inspect", "test")
c.Assert(err, checker.IsNil, check.Commentf(out))
vs = nil
err = json.Unmarshal([]byte(out), &vs)
c.Assert(err, checker.IsNil)
c.Assert(vs, checker.HasLen, 1)
c.Assert(vs[0].Options, checker.HasLen, 0)
c.Assert(vs[0].Driver, checker.Equals, "local")
} }
func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverUnmountOnMountFail(c *check.C) { func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverUnmountOnMountFail(c *check.C) {

View file

@ -284,56 +284,64 @@ func (s *VolumeStore) Create(name, driverName string, opts, labels map[string]st
func (s *VolumeStore) checkConflict(name, driverName string) (volume.Volume, error) { func (s *VolumeStore) checkConflict(name, driverName string) (volume.Volume, error) {
// check the local cache // check the local cache
v, _ := s.getNamed(name) v, _ := s.getNamed(name)
if v != nil { if v == nil {
vDriverName := v.DriverName() return nil, nil
if driverName != "" && vDriverName != driverName { }
// we have what looks like a conflict
// let's see if there are existing refs to this volume, if so we don't need
// to go any further since we can assume the volume is legit.
if len(s.getRefs(name)) > 0 {
return nil, errors.Wrapf(errNameConflict, "driver '%s' already has volume '%s'", vDriverName, name)
}
// looks like there is a conflict, but nothing is referencing it... vDriverName := v.DriverName()
// let's check if the found volume ref var conflict bool
// is stale by checking with the driver if it still exists if driverName != "" && vDriverName != driverName {
vd, err := volumedrivers.GetDriver(vDriverName) conflict = true
if err != nil { }
// play it safe and return the error
// TODO(cpuguy83): maybe when when v2 plugins are ubiquitous, we should
// just purge this from the cache
return nil, errors.Wrapf(errNameConflict, "found reference to volume '%s' in driver '%s', but got an error while checking the driver: %v", name, vDriverName, err)
}
// now check if it still exists in the driver // let's check if the found volume ref
v2, err := vd.Get(name) // is stale by checking with the driver if it still exists
err = errors.Cause(err) exists, err := volumeExists(v)
if err != nil { if err != nil {
if _, ok := err.(net.Error); ok { return nil, errors.Wrapf(errNameConflict, "found reference to volume '%s' in driver '%s', but got an error while checking the driver: %v", name, vDriverName, err)
// got some error related to the driver connectivity }
// play it safe and return the error
// TODO(cpuguy83): When when v2 plugins are ubiquitous, maybe we should
// just purge this from the cache
return nil, errors.Wrapf(errNameConflict, "found reference to volume '%s' in driver '%s', but got an error while checking the driver: %v", name, vDriverName, err)
}
// a driver can return whatever it wants, so let's make sure this is nil if exists {
if v2 == nil { if conflict {
// purge this reference from the cache return nil, errors.Wrapf(errNameConflict, "driver '%s' already has volume '%s'", vDriverName, name)
s.Purge(name)
return nil, nil
}
}
if v2 != nil {
return nil, errors.Wrapf(errNameConflict, "driver '%s' already has volume '%s'", vDriverName, name)
}
} }
return v, nil return v, nil
} }
if len(s.getRefs(v.Name())) > 0 {
// Containers are referencing this volume but it doesn't seem to exist anywhere.
// Return a conflict error here, the user can fix this with `docker volume rm -f`
return nil, errors.Wrapf(errNameConflict, "found references to volume '%s' in driver '%s' but the volume was not found in the driver -- you may need to remove containers referencing this volume or force remove the volume to re-create it", name, vDriverName)
}
// doesn't exist, so purge it from the cache
s.Purge(name)
return nil, nil return nil, nil
} }
// volumeExists returns if the volume is still present in the driver.
// An error is returned if there was an issue communicating with the driver.
func volumeExists(v volume.Volume) (bool, error) {
vd, err := volumedrivers.GetDriver(v.DriverName())
if err != nil {
return false, errors.Wrapf(err, "error while checking if volume %q exists in driver %q", v.Name(), v.DriverName())
}
exists, err := vd.Get(v.Name())
if err != nil {
err = errors.Cause(err)
if _, ok := err.(net.Error); ok {
return false, errors.Wrapf(err, "error while checking if volume %q exists in driver %q", v.Name(), v.DriverName())
}
// At this point, the error could be anything from the driver, such as "no such volume"
// Let's not check an error here, and instead check if the driver returned a volume
}
if exists == nil {
return false, nil
}
return true, nil
}
// create asks the given driver to create a volume with the name/opts. // create asks the given driver to create a volume with the name/opts.
// If a volume with the name is already known, it will ask the stored driver for the volume. // If a volume with the name is already known, it will ask the stored driver for the volume.
// If the passed in driver name does not match the driver name which is stored // If the passed in driver name does not match the driver name which is stored
@ -354,6 +362,7 @@ func (s *VolumeStore) create(name, driverName string, opts, labels map[string]st
if err != nil { if err != nil {
return nil, err return nil, err
} }
if v != nil { if v != nil {
return v, nil return v, nil
} }