In cases where the a plugin responds with both a null or empty volume
and a null or empty Err, the daemon would panic.
This is because we assumed the idiom if `err` is nil, then `v` must not
be but in reality the plugin may return whatever it wants and we want to
make sure it doesn't harm the daemon.
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
| ... | ... |
@@ -60,6 +60,7 @@ func (s *DockerExternalVolumeSuite) SetUpSuite(c *check.C) {
|
| 60 | 60 |
|
| 61 | 61 |
type pluginRequest struct {
|
| 62 | 62 |
Name string |
| 63 |
+ Opts map[string]string |
|
| 63 | 64 |
} |
| 64 | 65 |
|
| 65 | 66 |
type pluginResp struct {
|
| ... | ... |
@@ -70,6 +71,7 @@ func (s *DockerExternalVolumeSuite) SetUpSuite(c *check.C) {
|
| 70 | 70 |
type vol struct {
|
| 71 | 71 |
Name string |
| 72 | 72 |
Mountpoint string |
| 73 |
+ Ninja bool // hack used to trigger an null volume return on `Get` |
|
| 73 | 74 |
} |
| 74 | 75 |
var volList []vol |
| 75 | 76 |
|
| ... | ... |
@@ -107,7 +109,8 @@ func (s *DockerExternalVolumeSuite) SetUpSuite(c *check.C) {
|
| 107 | 107 |
send(w, err) |
| 108 | 108 |
return |
| 109 | 109 |
} |
| 110 |
- volList = append(volList, vol{Name: pr.Name})
|
|
| 110 |
+ _, isNinja := pr.Opts["ninja"] |
|
| 111 |
+ volList = append(volList, vol{Name: pr.Name, Ninja: isNinja})
|
|
| 111 | 112 |
send(w, nil) |
| 112 | 113 |
}) |
| 113 | 114 |
|
| ... | ... |
@@ -126,6 +129,10 @@ func (s *DockerExternalVolumeSuite) SetUpSuite(c *check.C) {
|
| 126 | 126 |
|
| 127 | 127 |
for _, v := range volList {
|
| 128 | 128 |
if v.Name == pr.Name {
|
| 129 |
+ if v.Ninja {
|
|
| 130 |
+ send(w, map[string]vol{})
|
|
| 131 |
+ return |
|
| 132 |
+ } |
|
| 129 | 133 |
v.Mountpoint = hostVolumePath(pr.Name) |
| 130 | 134 |
send(w, map[string]vol{"Volume": v})
|
| 131 | 135 |
return |
| ... | ... |
@@ -423,3 +430,12 @@ func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverWithDaemnRestart(c * |
| 423 | 423 |
c.Assert(mounts, checker.HasLen, 1) |
| 424 | 424 |
c.Assert(mounts[0].Driver, checker.Equals, "test-external-volume-driver") |
| 425 | 425 |
} |
| 426 |
+ |
|
| 427 |
+// Ensures that the daemon handles when the plugin responds to a `Get` request with a null volume and a null error. |
|
| 428 |
+// Prior the daemon would panic in this scenario. |
|
| 429 |
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverGetEmptyResponse(c *check.C) {
|
|
| 430 |
+ dockerCmd(c, "volume", "create", "-d", "test-external-volume-driver", "--name", "abc", "--opt", "ninja=1") |
|
| 431 |
+ out, _, err := dockerCmdWithError("volume", "inspect", "abc")
|
|
| 432 |
+ c.Assert(err, checker.NotNil, check.Commentf(out)) |
|
| 433 |
+ c.Assert(out, checker.Contains, "No such volume") |
|
| 434 |
+} |
| ... | ... |
@@ -1,6 +1,10 @@ |
| 1 | 1 |
package volumedrivers |
| 2 | 2 |
|
| 3 |
-import "github.com/docker/docker/volume" |
|
| 3 |
+import ( |
|
| 4 |
+ "fmt" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/docker/docker/volume" |
|
| 7 |
+) |
|
| 4 | 8 |
|
| 5 | 9 |
type volumeDriverAdapter struct {
|
| 6 | 10 |
name string |
| ... | ... |
@@ -49,6 +53,11 @@ func (a *volumeDriverAdapter) Get(name string) (volume.Volume, error) {
|
| 49 | 49 |
return nil, err |
| 50 | 50 |
} |
| 51 | 51 |
|
| 52 |
+ // plugin may have returned no volume and no error |
|
| 53 |
+ if v == nil {
|
|
| 54 |
+ return nil, fmt.Errorf("no such volume")
|
|
| 55 |
+ } |
|
| 56 |
+ |
|
| 52 | 57 |
return &volumeAdapter{
|
| 53 | 58 |
proxy: a.proxy, |
| 54 | 59 |
name: v.Name, |