Browse code

Fix panic when plugin responds with null volume

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>

Brian Goff authored on 2016/02/25 10:45:38
Showing 2 changed files
... ...
@@ -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,