Browse code

Move plugin client creation to the extension point

Signed-off-by: Brian Goff <cpuguy83@gmail.com>

Brian Goff authored on 2018/04/25 10:45:00
Showing 14 changed files
... ...
@@ -1859,6 +1859,13 @@ definitions:
1859 1859
                 type: "string"
1860 1860
                 x-nullable: false
1861 1861
                 example: "plugins.sock"
1862
+              ProtocolScheme:
1863
+                type: "string"
1864
+                example: "some.protocol/v1.0"
1865
+                description: "Protocol to use for clients connecting to the plugin."
1866
+                enum:
1867
+                  - ""
1868
+                  - "moby.plugins.http/v1"
1862 1869
           Entrypoint:
1863 1870
             type: "array"
1864 1871
             items:
... ...
@@ -121,6 +121,9 @@ type PluginConfigArgs struct {
121 121
 // swagger:model PluginConfigInterface
122 122
 type PluginConfigInterface struct {
123 123
 
124
+	// Protocol to use for clients connecting to the plugin.
125
+	ProtocolScheme string `json:"ProtocolScheme,omitempty"`
126
+
124 127
 	// socket
125 128
 	// Required: true
126 129
 	Socket string `json:"Socket"`
... ...
@@ -5,7 +5,9 @@ import (
5 5
 	"path/filepath"
6 6
 
7 7
 	"github.com/docker/docker/pkg/plugingetter"
8
+	"github.com/docker/docker/pkg/plugins"
8 9
 	"github.com/docker/docker/plugin/v2"
10
+	"github.com/pkg/errors"
9 11
 )
10 12
 
11 13
 func lookupPlugin(name string, pg plugingetter.PluginGetter, config Options) (Driver, error) {
... ...
@@ -28,6 +30,22 @@ func newPluginDriver(name string, pl plugingetter.CompatPlugin, config Options)
28 28
 			}
29 29
 		}
30 30
 	}
31
-	proxy := &graphDriverProxy{name, pl, Capabilities{}}
31
+
32
+	var proxy *graphDriverProxy
33
+
34
+	pa, ok := pl.(plugingetter.PluginAddr)
35
+	if !ok {
36
+		proxy = &graphDriverProxy{name, pl, Capabilities{}, pl.Client()}
37
+	} else {
38
+		if pa.Protocol() != plugins.ProtocolSchemeHTTPV1 {
39
+			return nil, errors.Errorf("plugin protocol not supported: %s", pa.Protocol())
40
+		}
41
+		addr := pa.Addr()
42
+		client, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pa.Timeout())
43
+		if err != nil {
44
+			return nil, errors.Wrap(err, "error creating plugin client")
45
+		}
46
+		proxy = &graphDriverProxy{name, pl, Capabilities{}, client}
47
+	}
32 48
 	return proxy, proxy.Init(filepath.Join(home, name), config.DriverOptions, config.UIDMaps, config.GIDMaps)
33 49
 }
... ...
@@ -13,9 +13,10 @@ import (
13 13
 )
14 14
 
15 15
 type graphDriverProxy struct {
16
-	name string
17
-	p    plugingetter.CompatPlugin
18
-	caps Capabilities
16
+	name   string
17
+	p      plugingetter.CompatPlugin
18
+	caps   Capabilities
19
+	client *plugins.Client
19 20
 }
20 21
 
21 22
 type graphDriverRequest struct {
... ...
@@ -57,7 +58,7 @@ func (d *graphDriverProxy) Init(home string, opts []string, uidMaps, gidMaps []i
57 57
 		GIDMaps: gidMaps,
58 58
 	}
59 59
 	var ret graphDriverResponse
60
-	if err := d.p.Client().Call("GraphDriver.Init", args, &ret); err != nil {
60
+	if err := d.client.Call("GraphDriver.Init", args, &ret); err != nil {
61 61
 		return err
62 62
 	}
63 63
 	if ret.Err != "" {
... ...
@@ -74,7 +75,7 @@ func (d *graphDriverProxy) Init(home string, opts []string, uidMaps, gidMaps []i
74 74
 func (d *graphDriverProxy) fetchCaps() (Capabilities, error) {
75 75
 	args := &graphDriverRequest{}
76 76
 	var ret graphDriverResponse
77
-	if err := d.p.Client().Call("GraphDriver.Capabilities", args, &ret); err != nil {
77
+	if err := d.client.Call("GraphDriver.Capabilities", args, &ret); err != nil {
78 78
 		if !plugins.IsNotFound(err) {
79 79
 			return Capabilities{}, err
80 80
 		}
... ...
@@ -108,7 +109,7 @@ func (d *graphDriverProxy) create(method, id, parent string, opts *CreateOpts) e
108 108
 		args.StorageOpt = opts.StorageOpt
109 109
 	}
110 110
 	var ret graphDriverResponse
111
-	if err := d.p.Client().Call(method, args, &ret); err != nil {
111
+	if err := d.client.Call(method, args, &ret); err != nil {
112 112
 		return err
113 113
 	}
114 114
 	if ret.Err != "" {
... ...
@@ -120,7 +121,7 @@ func (d *graphDriverProxy) create(method, id, parent string, opts *CreateOpts) e
120 120
 func (d *graphDriverProxy) Remove(id string) error {
121 121
 	args := &graphDriverRequest{ID: id}
122 122
 	var ret graphDriverResponse
123
-	if err := d.p.Client().Call("GraphDriver.Remove", args, &ret); err != nil {
123
+	if err := d.client.Call("GraphDriver.Remove", args, &ret); err != nil {
124 124
 		return err
125 125
 	}
126 126
 	if ret.Err != "" {
... ...
@@ -135,7 +136,7 @@ func (d *graphDriverProxy) Get(id, mountLabel string) (containerfs.ContainerFS,
135 135
 		MountLabel: mountLabel,
136 136
 	}
137 137
 	var ret graphDriverResponse
138
-	if err := d.p.Client().Call("GraphDriver.Get", args, &ret); err != nil {
138
+	if err := d.client.Call("GraphDriver.Get", args, &ret); err != nil {
139 139
 		return nil, err
140 140
 	}
141 141
 	var err error
... ...
@@ -148,7 +149,7 @@ func (d *graphDriverProxy) Get(id, mountLabel string) (containerfs.ContainerFS,
148 148
 func (d *graphDriverProxy) Put(id string) error {
149 149
 	args := &graphDriverRequest{ID: id}
150 150
 	var ret graphDriverResponse
151
-	if err := d.p.Client().Call("GraphDriver.Put", args, &ret); err != nil {
151
+	if err := d.client.Call("GraphDriver.Put", args, &ret); err != nil {
152 152
 		return err
153 153
 	}
154 154
 	if ret.Err != "" {
... ...
@@ -160,7 +161,7 @@ func (d *graphDriverProxy) Put(id string) error {
160 160
 func (d *graphDriverProxy) Exists(id string) bool {
161 161
 	args := &graphDriverRequest{ID: id}
162 162
 	var ret graphDriverResponse
163
-	if err := d.p.Client().Call("GraphDriver.Exists", args, &ret); err != nil {
163
+	if err := d.client.Call("GraphDriver.Exists", args, &ret); err != nil {
164 164
 		return false
165 165
 	}
166 166
 	return ret.Exists
... ...
@@ -169,7 +170,7 @@ func (d *graphDriverProxy) Exists(id string) bool {
169 169
 func (d *graphDriverProxy) Status() [][2]string {
170 170
 	args := &graphDriverRequest{}
171 171
 	var ret graphDriverResponse
172
-	if err := d.p.Client().Call("GraphDriver.Status", args, &ret); err != nil {
172
+	if err := d.client.Call("GraphDriver.Status", args, &ret); err != nil {
173 173
 		return nil
174 174
 	}
175 175
 	return ret.Status
... ...
@@ -180,7 +181,7 @@ func (d *graphDriverProxy) GetMetadata(id string) (map[string]string, error) {
180 180
 		ID: id,
181 181
 	}
182 182
 	var ret graphDriverResponse
183
-	if err := d.p.Client().Call("GraphDriver.GetMetadata", args, &ret); err != nil {
183
+	if err := d.client.Call("GraphDriver.GetMetadata", args, &ret); err != nil {
184 184
 		return nil, err
185 185
 	}
186 186
 	if ret.Err != "" {
... ...
@@ -199,7 +200,7 @@ func (d *graphDriverProxy) Cleanup() error {
199 199
 
200 200
 	args := &graphDriverRequest{}
201 201
 	var ret graphDriverResponse
202
-	if err := d.p.Client().Call("GraphDriver.Cleanup", args, &ret); err != nil {
202
+	if err := d.client.Call("GraphDriver.Cleanup", args, &ret); err != nil {
203 203
 		return nil
204 204
 	}
205 205
 	if ret.Err != "" {
... ...
@@ -213,7 +214,7 @@ func (d *graphDriverProxy) Diff(id, parent string) (io.ReadCloser, error) {
213 213
 		ID:     id,
214 214
 		Parent: parent,
215 215
 	}
216
-	body, err := d.p.Client().Stream("GraphDriver.Diff", args)
216
+	body, err := d.client.Stream("GraphDriver.Diff", args)
217 217
 	if err != nil {
218 218
 		return nil, err
219 219
 	}
... ...
@@ -226,7 +227,7 @@ func (d *graphDriverProxy) Changes(id, parent string) ([]archive.Change, error)
226 226
 		Parent: parent,
227 227
 	}
228 228
 	var ret graphDriverResponse
229
-	if err := d.p.Client().Call("GraphDriver.Changes", args, &ret); err != nil {
229
+	if err := d.client.Call("GraphDriver.Changes", args, &ret); err != nil {
230 230
 		return nil, err
231 231
 	}
232 232
 	if ret.Err != "" {
... ...
@@ -238,7 +239,7 @@ func (d *graphDriverProxy) Changes(id, parent string) ([]archive.Change, error)
238 238
 
239 239
 func (d *graphDriverProxy) ApplyDiff(id, parent string, diff io.Reader) (int64, error) {
240 240
 	var ret graphDriverResponse
241
-	if err := d.p.Client().SendFile(fmt.Sprintf("GraphDriver.ApplyDiff?id=%s&parent=%s", id, parent), diff, &ret); err != nil {
241
+	if err := d.client.SendFile(fmt.Sprintf("GraphDriver.ApplyDiff?id=%s&parent=%s", id, parent), diff, &ret); err != nil {
242 242
 		return -1, err
243 243
 	}
244 244
 	if ret.Err != "" {
... ...
@@ -253,7 +254,7 @@ func (d *graphDriverProxy) DiffSize(id, parent string) (int64, error) {
253 253
 		Parent: parent,
254 254
 	}
255 255
 	var ret graphDriverResponse
256
-	if err := d.p.Client().Call("GraphDriver.DiffSize", args, &ret); err != nil {
256
+	if err := d.client.Call("GraphDriver.DiffSize", args, &ret); err != nil {
257 257
 		return -1, err
258 258
 	}
259 259
 	if ret.Err != "" {
... ...
@@ -8,6 +8,7 @@ import (
8 8
 
9 9
 	"github.com/docker/docker/api/types/plugins/logdriver"
10 10
 	getter "github.com/docker/docker/pkg/plugingetter"
11
+	"github.com/docker/docker/pkg/plugins"
11 12
 	"github.com/docker/docker/pkg/stringid"
12 13
 	"github.com/pkg/errors"
13 14
 )
... ...
@@ -37,11 +38,32 @@ func getPlugin(name string, mode int) (Creator, error) {
37 37
 		return nil, fmt.Errorf("error looking up logging plugin %s: %v", name, err)
38 38
 	}
39 39
 
40
-	d := &logPluginProxy{p.Client()}
41
-	return makePluginCreator(name, d, p.ScopedPath), nil
40
+	client, err := makePluginClient(p)
41
+	if err != nil {
42
+		return nil, err
43
+	}
44
+	return makePluginCreator(name, client, p.ScopedPath), nil
45
+}
46
+
47
+func makePluginClient(p getter.CompatPlugin) (logPlugin, error) {
48
+	pa, ok := p.(getter.PluginAddr)
49
+	if !ok {
50
+		return &logPluginProxy{p.Client()}, nil
51
+	}
52
+
53
+	if pa.Protocol() != plugins.ProtocolSchemeHTTPV1 {
54
+		return nil, errors.Errorf("plugin protocol not supported: %s", p)
55
+	}
56
+
57
+	addr := pa.Addr()
58
+	c, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pa.Timeout())
59
+	if err != nil {
60
+		return nil, errors.Wrap(err, "error making plugin client")
61
+	}
62
+	return &logPluginProxy{c}, nil
42 63
 }
43 64
 
44
-func makePluginCreator(name string, l *logPluginProxy, scopePath func(s string) string) Creator {
65
+func makePluginCreator(name string, l logPlugin, scopePath func(s string) string) Creator {
45 66
 	return func(logCtx Info) (logger Logger, err error) {
46 67
 		defer func() {
47 68
 			if err != nil {
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"sync"
5 5
 
6 6
 	"github.com/docker/docker/pkg/plugingetter"
7
+	"github.com/docker/docker/pkg/plugins"
7 8
 	"github.com/docker/go-metrics"
8 9
 	"github.com/pkg/errors"
9 10
 	"github.com/prometheus/client_golang/prometheus"
... ...
@@ -118,7 +119,15 @@ func (d *Daemon) cleanupMetricsPlugins() {
118 118
 		p := plugin
119 119
 		go func() {
120 120
 			defer wg.Done()
121
-			pluginStopMetricsCollection(p)
121
+
122
+			adapter, err := makePluginAdapter(p)
123
+			if err != nil {
124
+				logrus.WithError(err).WithField("plugin", p.Name()).Error("Error creating metrics plugin adapater")
125
+				return
126
+			}
127
+			if err := adapter.StopMetrics(); err != nil {
128
+				logrus.WithError(err).WithField("plugin", p.Name()).Error("Error stopping plugin metrics collection")
129
+			}
122 130
 		}()
123 131
 	}
124 132
 	wg.Wait()
... ...
@@ -128,12 +137,39 @@ func (d *Daemon) cleanupMetricsPlugins() {
128 128
 	}
129 129
 }
130 130
 
131
-func pluginStartMetricsCollection(p plugingetter.CompatPlugin) error {
131
+type metricsPlugin interface {
132
+	StartMetrics() error
133
+	StopMetrics() error
134
+}
135
+
136
+func makePluginAdapter(p plugingetter.CompatPlugin) (metricsPlugin, error) {
137
+	pa, ok := p.(plugingetter.PluginAddr)
138
+	if !ok {
139
+		return &metricsPluginAdapter{p.Client(), p.Name()}, nil
140
+	}
141
+	if pa.Protocol() != plugins.ProtocolSchemeHTTPV1 {
142
+		return nil, errors.Errorf("plugin protocol not supported: %s", pa.Protocol())
143
+	}
144
+
145
+	addr := pa.Addr()
146
+	client, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pa.Timeout())
147
+	if err != nil {
148
+		return nil, errors.Wrap(err, "error creating metrics plugin client")
149
+	}
150
+	return &metricsPluginAdapter{client, p.Name()}, nil
151
+}
152
+
153
+type metricsPluginAdapter struct {
154
+	c    *plugins.Client
155
+	name string
156
+}
157
+
158
+func (a *metricsPluginAdapter) StartMetrics() error {
132 159
 	type metricsPluginResponse struct {
133 160
 		Err string
134 161
 	}
135 162
 	var res metricsPluginResponse
136
-	if err := p.Client().Call(metricsPluginType+".StartMetrics", nil, &res); err != nil {
163
+	if err := a.c.Call(metricsPluginType+".StartMetrics", nil, &res); err != nil {
137 164
 		return errors.Wrap(err, "could not start metrics plugin")
138 165
 	}
139 166
 	if res.Err != "" {
... ...
@@ -142,8 +178,9 @@ func pluginStartMetricsCollection(p plugingetter.CompatPlugin) error {
142 142
 	return nil
143 143
 }
144 144
 
145
-func pluginStopMetricsCollection(p plugingetter.CompatPlugin) {
146
-	if err := p.Client().Call(metricsPluginType+".StopMetrics", nil, nil); err != nil {
147
-		logrus.WithError(err).WithField("name", p.Name()).Error("error stopping metrics collector")
145
+func (a *metricsPluginAdapter) StopMetrics() error {
146
+	if err := a.c.Call(metricsPluginType+".StopMetrics", nil, nil); err != nil {
147
+		return errors.Wrap(err, "error stopping metrics collector")
148 148
 	}
149
+	return nil
149 150
 }
... ...
@@ -49,8 +49,12 @@ func registerMetricsPluginCallback(store *plugin.Store, sockPath string) {
49 49
 			return
50 50
 		}
51 51
 
52
-		if err := pluginStartMetricsCollection(p); err != nil {
53
-			logrus.WithError(err).WithField("name", name).Error("error while initializing metrics plugin")
52
+		adapter, err := makePluginAdapter(p)
53
+		if err != nil {
54
+			logrus.WithError(err).WithField("plugin", p.Name()).Error("Error creating plugin adapater")
55
+		}
56
+		if err := adapter.StartMetrics(); err != nil {
57
+			logrus.WithError(err).WithField("plugin", p.Name()).Error("Error starting metrics collector plugin")
54 58
 		}
55 59
 	})
56 60
 }
... ...
@@ -1,6 +1,9 @@
1 1
 package plugingetter // import "github.com/docker/docker/pkg/plugingetter"
2 2
 
3 3
 import (
4
+	"net"
5
+	"time"
6
+
4 7
 	"github.com/docker/docker/pkg/plugins"
5 8
 )
6 9
 
... ...
@@ -21,6 +24,14 @@ type CompatPlugin interface {
21 21
 	IsV1() bool
22 22
 }
23 23
 
24
+// PluginAddr is a plugin that exposes the socket address for creating custom clients rather than the built-in `*plugins.Client`
25
+type PluginAddr interface {
26
+	CompatPlugin
27
+	Addr() net.Addr
28
+	Timeout() time.Duration
29
+	Protocol() string
30
+}
31
+
24 32
 // CountedPlugin is a plugin which is reference counted.
25 33
 type CountedPlugin interface {
26 34
 	Acquire()
... ...
@@ -31,6 +31,9 @@ import (
31 31
 	"github.com/sirupsen/logrus"
32 32
 )
33 33
 
34
+// ProtocolSchemeHTTPV1 is the name of the protocol used for interacting with plugins using this package.
35
+const ProtocolSchemeHTTPV1 = "moby.plugins.http/v1"
36
+
34 37
 var (
35 38
 	// ErrNotImplements is returned if the plugin does not implement the requested driver.
36 39
 	ErrNotImplements = errors.New("Plugin does not implement the requested driver")
... ...
@@ -88,6 +91,11 @@ func (p *Plugin) Client() *Client {
88 88
 	return p.client
89 89
 }
90 90
 
91
+// Protocol returns the protocol name/version used for plugins in this package.
92
+func (p *Plugin) Protocol() string {
93
+	return ProtocolSchemeHTTPV1
94
+}
95
+
91 96
 // IsV1 returns true for V1 plugins and false otherwise.
92 97
 func (p *Plugin) IsV1() bool {
93 98
 	return true
... ...
@@ -71,14 +71,20 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
71 71
 
72 72
 func (pm *Manager) pluginPostStart(p *v2.Plugin, c *controller) error {
73 73
 	sockAddr := filepath.Join(pm.config.ExecRoot, p.GetID(), p.GetSocket())
74
-	client, err := plugins.NewClientWithTimeout("unix://"+sockAddr, nil, time.Duration(c.timeoutInSecs)*time.Second)
75
-	if err != nil {
76
-		c.restart = false
77
-		shutdownPlugin(p, c, pm.executor)
78
-		return errors.WithStack(err)
79
-	}
74
+	p.SetTimeout(time.Duration(c.timeoutInSecs) * time.Second)
75
+	addr := &net.UnixAddr{Net: "unix", Name: sockAddr}
76
+	p.SetAddr(addr)
80 77
 
81
-	p.SetPClient(client)
78
+	if p.Protocol() == plugins.ProtocolSchemeHTTPV1 {
79
+		client, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, p.Timeout())
80
+		if err != nil {
81
+			c.restart = false
82
+			shutdownPlugin(p, c, pm.executor)
83
+			return errors.WithStack(err)
84
+		}
85
+
86
+		p.SetPClient(client)
87
+	}
82 88
 
83 89
 	// Initial sleep before net Dial to allow plugin to listen on socket.
84 90
 	time.Sleep(500 * time.Millisecond)
... ...
@@ -2,9 +2,11 @@ package v2 // import "github.com/docker/docker/plugin/v2"
2 2
 
3 3
 import (
4 4
 	"fmt"
5
+	"net"
5 6
 	"path/filepath"
6 7
 	"strings"
7 8
 	"sync"
9
+	"time"
8 10
 
9 11
 	"github.com/docker/docker/api/types"
10 12
 	"github.com/docker/docker/pkg/plugingetter"
... ...
@@ -27,6 +29,8 @@ type Plugin struct {
27 27
 	modifyRuntimeSpec func(*specs.Spec)
28 28
 
29 29
 	SwarmServiceID string
30
+	timeout        time.Duration
31
+	addr           net.Addr
30 32
 }
31 33
 
32 34
 const defaultPluginRuntimeDestination = "/run/docker/plugins"
... ...
@@ -50,6 +54,7 @@ func (p *Plugin) ScopedPath(s string) string {
50 50
 }
51 51
 
52 52
 // Client returns the plugin client.
53
+// Deprecated: use p.Addr() and manually create the client
53 54
 func (p *Plugin) Client() *plugins.Client {
54 55
 	p.mu.RLock()
55 56
 	defer p.mu.RUnlock()
... ...
@@ -58,6 +63,7 @@ func (p *Plugin) Client() *plugins.Client {
58 58
 }
59 59
 
60 60
 // SetPClient set the plugin client.
61
+// Deprecated: Hardcoded plugin client is deprecated
61 62
 func (p *Plugin) SetPClient(client *plugins.Client) {
62 63
 	p.mu.Lock()
63 64
 	defer p.mu.Unlock()
... ...
@@ -264,3 +270,42 @@ func (p *Plugin) SetSpecOptModifier(f func(*specs.Spec)) {
264 264
 	p.modifyRuntimeSpec = f
265 265
 	p.mu.Unlock()
266 266
 }
267
+
268
+// Timeout gets the currently configured connection timeout.
269
+// This should be used when dialing the plugin.
270
+func (p *Plugin) Timeout() time.Duration {
271
+	p.mu.RLock()
272
+	t := p.timeout
273
+	p.mu.RUnlock()
274
+	return t
275
+}
276
+
277
+// SetTimeout sets the timeout to use for dialing.
278
+func (p *Plugin) SetTimeout(t time.Duration) {
279
+	p.mu.Lock()
280
+	p.timeout = t
281
+	p.mu.Unlock()
282
+}
283
+
284
+// Addr returns the net.Addr to use to connect to the plugin socket
285
+func (p *Plugin) Addr() net.Addr {
286
+	p.mu.RLock()
287
+	addr := p.addr
288
+	p.mu.RUnlock()
289
+	return addr
290
+}
291
+
292
+// SetAddr sets the plugin address which can be used for dialing the plugin.
293
+func (p *Plugin) SetAddr(addr net.Addr) {
294
+	p.mu.Lock()
295
+	p.addr = addr
296
+	p.mu.Unlock()
297
+}
298
+
299
+// Protocol is the protocol that should be used for interacting with the plugin.
300
+func (p *Plugin) Protocol() string {
301
+	if p.PluginObj.Config.Interface.ProtocolScheme != "" {
302
+		return p.PluginObj.Config.Interface.ProtocolScheme
303
+	}
304
+	return plugins.ProtocolSchemeHTTPV1
305
+}
... ...
@@ -5,7 +5,7 @@ package v2 // import "github.com/docker/docker/plugin/v2"
5 5
 import (
6 6
 	"errors"
7 7
 
8
-	specs "github.com/opencontainers/runtime-spec/specs-go"
8
+	"github.com/opencontainers/runtime-spec/specs-go"
9 9
 )
10 10
 
11 11
 // InitSpec creates an OCI spec from the plugin's config.
... ...
@@ -17,7 +17,7 @@ type volumeDriverAdapter struct {
17 17
 	name         string
18 18
 	scopePath    func(s string) string
19 19
 	capabilities *volume.Capability
20
-	proxy        *volumeDriverProxy
20
+	proxy        volumeDriver
21 21
 }
22 22
 
23 23
 func (a *volumeDriverAdapter) Name() string {
... ...
@@ -114,7 +114,7 @@ func (a *volumeDriverAdapter) getCapabilities() volume.Capability {
114 114
 }
115 115
 
116 116
 type volumeAdapter struct {
117
-	proxy      *volumeDriverProxy
117
+	proxy      volumeDriver
118 118
 	name       string
119 119
 	scopePath  func(string) string
120 120
 	driverName string
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"github.com/docker/docker/errdefs"
11 11
 	"github.com/docker/docker/pkg/locker"
12 12
 	getter "github.com/docker/docker/pkg/plugingetter"
13
+	"github.com/docker/docker/pkg/plugins"
13 14
 	"github.com/docker/docker/volume"
14 15
 	"github.com/pkg/errors"
15 16
 	"github.com/sirupsen/logrus"
... ...
@@ -17,12 +18,6 @@ import (
17 17
 
18 18
 const extName = "VolumeDriver"
19 19
 
20
-// NewVolumeDriver returns a driver has the given name mapped on the given client.
21
-func NewVolumeDriver(name string, scopePath func(string) string, c client) volume.Driver {
22
-	proxy := &volumeDriverProxy{c}
23
-	return &volumeDriverAdapter{name: name, scopePath: scopePath, proxy: proxy}
24
-}
25
-
26 20
 // volumeDriver defines the available functions that volume plugins must implement.
27 21
 // This interface is only defined to generate the proxy objects.
28 22
 // It's not intended to be public or reused.
... ...
@@ -93,7 +88,10 @@ func (s *Store) lookup(name string, mode int) (volume.Driver, error) {
93 93
 			return nil, errors.Wrap(err, "error looking up volume plugin "+name)
94 94
 		}
95 95
 
96
-		d := NewVolumeDriver(p.Name(), p.ScopedPath, p.Client())
96
+		d, err := makePluginAdapter(p)
97
+		if err != nil {
98
+			return nil, errors.Wrap(err, "error making plugin client")
99
+		}
97 100
 		if err := validateDriver(d); err != nil {
98 101
 			if mode > 0 {
99 102
 				// Undo any reference count changes from the initial `Get`
... ...
@@ -201,7 +199,10 @@ func (s *Store) GetAllDrivers() ([]volume.Driver, error) {
201 201
 			continue
202 202
 		}
203 203
 
204
-		ext := NewVolumeDriver(name, p.ScopedPath, p.Client())
204
+		ext, err := makePluginAdapter(p)
205
+		if err != nil {
206
+			return nil, errors.Wrap(err, "error making plugin client")
207
+		}
205 208
 		if p.IsV1() {
206 209
 			s.extensions[name] = ext
207 210
 		}
... ...
@@ -209,3 +210,22 @@ func (s *Store) GetAllDrivers() ([]volume.Driver, error) {
209 209
 	}
210 210
 	return ds, nil
211 211
 }
212
+
213
+func makePluginAdapter(p getter.CompatPlugin) (*volumeDriverAdapter, error) {
214
+	pa, ok := p.(getter.PluginAddr)
215
+	if !ok {
216
+		return &volumeDriverAdapter{name: p.Name(), scopePath: p.ScopedPath, proxy: &volumeDriverProxy{p.Client()}}, nil
217
+	}
218
+
219
+	if pa.Protocol() != plugins.ProtocolSchemeHTTPV1 {
220
+		return nil, errors.Errorf("plugin protocol not supported: %s", p)
221
+	}
222
+
223
+	addr := pa.Addr()
224
+	client, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pa.Timeout())
225
+	if err != nil {
226
+		return nil, errors.Wrap(err, "error creating plugin client")
227
+	}
228
+
229
+	return &volumeDriverAdapter{name: p.Name(), scopePath: p.ScopedPath, proxy: &volumeDriverProxy{client}}, nil
230
+}