Browse code

Migrate test-integration-cli experimental plugin tests to integration

All `plugins` tests that require an `ExperimentalDaemon` are migrated
to `integration/plugin/*` and start an experimental daemon to test on
it.

The end goal being to remove the `experimental` build.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>

Vincent Demeester authored on 2018/04/19 18:57:28
Showing 5 changed files
... ...
@@ -231,24 +231,6 @@ func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
231 231
 	c.Assert(err, checker.IsNil, check.Commentf(out))
232 232
 }
233 233
 
234
-func (s *DockerDaemonSuite) TestGraphdriverPlugin(c *check.C) {
235
-	testRequires(c, Network, IsAmd64, DaemonIsLinux, overlay2Supported, ExperimentalDaemon)
236
-
237
-	s.d.Start(c)
238
-
239
-	// install the plugin
240
-	plugin := "cpuguy83/docker-overlay2-graphdriver-plugin"
241
-	out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", plugin)
242
-	c.Assert(err, checker.IsNil, check.Commentf(out))
243
-
244
-	// restart the daemon with the plugin set as the storage driver
245
-	s.d.Restart(c, "-s", plugin, "--storage-opt", "overlay2.override_kernel_check=1")
246
-
247
-	// run a container
248
-	out, err = s.d.Cmd("run", "--rm", "busybox", "true") // this will pull busybox using the plugin
249
-	c.Assert(err, checker.IsNil, check.Commentf(out))
250
-}
251
-
252 234
 func (s *DockerDaemonSuite) TestPluginVolumeRemoveOnRestart(c *check.C) {
253 235
 	testRequires(c, DaemonIsLinux, Network, IsAmd64)
254 236
 
255 237
deleted file mode 100644
... ...
@@ -1,406 +0,0 @@
1
-// +build !windows
2
-
3
-package main
4
-
5
-import (
6
-	"encoding/json"
7
-	"fmt"
8
-	"io"
9
-	"io/ioutil"
10
-	"net/http"
11
-	"net/http/httptest"
12
-	"os"
13
-	"strings"
14
-
15
-	"github.com/docker/docker/daemon/graphdriver"
16
-	"github.com/docker/docker/daemon/graphdriver/vfs"
17
-	"github.com/docker/docker/integration-cli/daemon"
18
-	testdaemon "github.com/docker/docker/internal/test/daemon"
19
-	"github.com/docker/docker/pkg/archive"
20
-	"github.com/docker/docker/pkg/plugins"
21
-	"github.com/go-check/check"
22
-)
23
-
24
-func init() {
25
-	check.Suite(&DockerExternalGraphdriverSuite{
26
-		ds: &DockerSuite{},
27
-	})
28
-}
29
-
30
-type DockerExternalGraphdriverSuite struct {
31
-	server  *httptest.Server
32
-	jserver *httptest.Server
33
-	ds      *DockerSuite
34
-	d       *daemon.Daemon
35
-	ec      map[string]*graphEventsCounter
36
-}
37
-
38
-type graphEventsCounter struct {
39
-	activations int
40
-	creations   int
41
-	removals    int
42
-	gets        int
43
-	puts        int
44
-	stats       int
45
-	cleanups    int
46
-	exists      int
47
-	init        int
48
-	metadata    int
49
-	diff        int
50
-	applydiff   int
51
-	changes     int
52
-	diffsize    int
53
-}
54
-
55
-func (s *DockerExternalGraphdriverSuite) SetUpTest(c *check.C) {
56
-	s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
57
-}
58
-
59
-func (s *DockerExternalGraphdriverSuite) OnTimeout(c *check.C) {
60
-	s.d.DumpStackAndQuit()
61
-}
62
-
63
-func (s *DockerExternalGraphdriverSuite) TearDownTest(c *check.C) {
64
-	if s.d != nil {
65
-		s.d.Stop(c)
66
-		s.ds.TearDownTest(c)
67
-	}
68
-}
69
-
70
-func (s *DockerExternalGraphdriverSuite) SetUpSuite(c *check.C) {
71
-	s.ec = make(map[string]*graphEventsCounter)
72
-	s.setUpPluginViaSpecFile(c)
73
-	s.setUpPluginViaJSONFile(c)
74
-}
75
-
76
-func (s *DockerExternalGraphdriverSuite) setUpPluginViaSpecFile(c *check.C) {
77
-	mux := http.NewServeMux()
78
-	s.server = httptest.NewServer(mux)
79
-
80
-	s.setUpPlugin(c, "test-external-graph-driver", "spec", mux, []byte(s.server.URL))
81
-}
82
-
83
-func (s *DockerExternalGraphdriverSuite) setUpPluginViaJSONFile(c *check.C) {
84
-	mux := http.NewServeMux()
85
-	s.jserver = httptest.NewServer(mux)
86
-
87
-	p := plugins.NewLocalPlugin("json-external-graph-driver", s.jserver.URL)
88
-	b, err := json.Marshal(p)
89
-	c.Assert(err, check.IsNil)
90
-
91
-	s.setUpPlugin(c, "json-external-graph-driver", "json", mux, b)
92
-}
93
-
94
-func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ext string, mux *http.ServeMux, b []byte) {
95
-	type graphDriverRequest struct {
96
-		ID         string `json:",omitempty"`
97
-		Parent     string `json:",omitempty"`
98
-		MountLabel string `json:",omitempty"`
99
-		ReadOnly   bool   `json:",omitempty"`
100
-	}
101
-
102
-	type graphDriverResponse struct {
103
-		Err      error             `json:",omitempty"`
104
-		Dir      string            `json:",omitempty"`
105
-		Exists   bool              `json:",omitempty"`
106
-		Status   [][2]string       `json:",omitempty"`
107
-		Metadata map[string]string `json:",omitempty"`
108
-		Changes  []archive.Change  `json:",omitempty"`
109
-		Size     int64             `json:",omitempty"`
110
-	}
111
-
112
-	respond := func(w http.ResponseWriter, data interface{}) {
113
-		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
114
-		switch t := data.(type) {
115
-		case error:
116
-			fmt.Fprintln(w, fmt.Sprintf(`{"Err": %q}`, t.Error()))
117
-		case string:
118
-			fmt.Fprintln(w, t)
119
-		default:
120
-			json.NewEncoder(w).Encode(&data)
121
-		}
122
-	}
123
-
124
-	decReq := func(b io.ReadCloser, out interface{}, w http.ResponseWriter) error {
125
-		defer b.Close()
126
-		if err := json.NewDecoder(b).Decode(&out); err != nil {
127
-			http.Error(w, fmt.Sprintf("error decoding json: %s", err.Error()), 500)
128
-		}
129
-		return nil
130
-	}
131
-
132
-	base, err := ioutil.TempDir("", name)
133
-	c.Assert(err, check.IsNil)
134
-	vfsProto, err := vfs.Init(base, []string{}, nil, nil)
135
-	c.Assert(err, check.IsNil, check.Commentf("error initializing graph driver"))
136
-	driver := graphdriver.NewNaiveDiffDriver(vfsProto, nil, nil)
137
-
138
-	s.ec[ext] = &graphEventsCounter{}
139
-	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
140
-		s.ec[ext].activations++
141
-		respond(w, `{"Implements": ["GraphDriver"]}`)
142
-	})
143
-
144
-	mux.HandleFunc("/GraphDriver.Init", func(w http.ResponseWriter, r *http.Request) {
145
-		s.ec[ext].init++
146
-		respond(w, "{}")
147
-	})
148
-
149
-	mux.HandleFunc("/GraphDriver.CreateReadWrite", func(w http.ResponseWriter, r *http.Request) {
150
-		s.ec[ext].creations++
151
-
152
-		var req graphDriverRequest
153
-		if err := decReq(r.Body, &req, w); err != nil {
154
-			return
155
-		}
156
-		if err := driver.CreateReadWrite(req.ID, req.Parent, nil); err != nil {
157
-			respond(w, err)
158
-			return
159
-		}
160
-		respond(w, "{}")
161
-	})
162
-
163
-	mux.HandleFunc("/GraphDriver.Create", func(w http.ResponseWriter, r *http.Request) {
164
-		s.ec[ext].creations++
165
-
166
-		var req graphDriverRequest
167
-		if err := decReq(r.Body, &req, w); err != nil {
168
-			return
169
-		}
170
-		if err := driver.Create(req.ID, req.Parent, nil); err != nil {
171
-			respond(w, err)
172
-			return
173
-		}
174
-		respond(w, "{}")
175
-	})
176
-
177
-	mux.HandleFunc("/GraphDriver.Remove", func(w http.ResponseWriter, r *http.Request) {
178
-		s.ec[ext].removals++
179
-
180
-		var req graphDriverRequest
181
-		if err := decReq(r.Body, &req, w); err != nil {
182
-			return
183
-		}
184
-
185
-		if err := driver.Remove(req.ID); err != nil {
186
-			respond(w, err)
187
-			return
188
-		}
189
-		respond(w, "{}")
190
-	})
191
-
192
-	mux.HandleFunc("/GraphDriver.Get", func(w http.ResponseWriter, r *http.Request) {
193
-		s.ec[ext].gets++
194
-
195
-		var req graphDriverRequest
196
-		if err := decReq(r.Body, &req, w); err != nil {
197
-			return
198
-		}
199
-
200
-		// TODO @gupta-ak: Figure out what to do here.
201
-		dir, err := driver.Get(req.ID, req.MountLabel)
202
-		if err != nil {
203
-			respond(w, err)
204
-			return
205
-		}
206
-		respond(w, &graphDriverResponse{Dir: dir.Path()})
207
-	})
208
-
209
-	mux.HandleFunc("/GraphDriver.Put", func(w http.ResponseWriter, r *http.Request) {
210
-		s.ec[ext].puts++
211
-
212
-		var req graphDriverRequest
213
-		if err := decReq(r.Body, &req, w); err != nil {
214
-			return
215
-		}
216
-
217
-		if err := driver.Put(req.ID); err != nil {
218
-			respond(w, err)
219
-			return
220
-		}
221
-		respond(w, "{}")
222
-	})
223
-
224
-	mux.HandleFunc("/GraphDriver.Exists", func(w http.ResponseWriter, r *http.Request) {
225
-		s.ec[ext].exists++
226
-
227
-		var req graphDriverRequest
228
-		if err := decReq(r.Body, &req, w); err != nil {
229
-			return
230
-		}
231
-		respond(w, &graphDriverResponse{Exists: driver.Exists(req.ID)})
232
-	})
233
-
234
-	mux.HandleFunc("/GraphDriver.Status", func(w http.ResponseWriter, r *http.Request) {
235
-		s.ec[ext].stats++
236
-		respond(w, &graphDriverResponse{Status: driver.Status()})
237
-	})
238
-
239
-	mux.HandleFunc("/GraphDriver.Cleanup", func(w http.ResponseWriter, r *http.Request) {
240
-		s.ec[ext].cleanups++
241
-		err := driver.Cleanup()
242
-		if err != nil {
243
-			respond(w, err)
244
-			return
245
-		}
246
-		respond(w, `{}`)
247
-	})
248
-
249
-	mux.HandleFunc("/GraphDriver.GetMetadata", func(w http.ResponseWriter, r *http.Request) {
250
-		s.ec[ext].metadata++
251
-
252
-		var req graphDriverRequest
253
-		if err := decReq(r.Body, &req, w); err != nil {
254
-			return
255
-		}
256
-
257
-		data, err := driver.GetMetadata(req.ID)
258
-		if err != nil {
259
-			respond(w, err)
260
-			return
261
-		}
262
-		respond(w, &graphDriverResponse{Metadata: data})
263
-	})
264
-
265
-	mux.HandleFunc("/GraphDriver.Diff", func(w http.ResponseWriter, r *http.Request) {
266
-		s.ec[ext].diff++
267
-
268
-		var req graphDriverRequest
269
-		if err := decReq(r.Body, &req, w); err != nil {
270
-			return
271
-		}
272
-
273
-		diff, err := driver.Diff(req.ID, req.Parent)
274
-		if err != nil {
275
-			respond(w, err)
276
-			return
277
-		}
278
-		io.Copy(w, diff)
279
-	})
280
-
281
-	mux.HandleFunc("/GraphDriver.Changes", func(w http.ResponseWriter, r *http.Request) {
282
-		s.ec[ext].changes++
283
-		var req graphDriverRequest
284
-		if err := decReq(r.Body, &req, w); err != nil {
285
-			return
286
-		}
287
-
288
-		changes, err := driver.Changes(req.ID, req.Parent)
289
-		if err != nil {
290
-			respond(w, err)
291
-			return
292
-		}
293
-		respond(w, &graphDriverResponse{Changes: changes})
294
-	})
295
-
296
-	mux.HandleFunc("/GraphDriver.ApplyDiff", func(w http.ResponseWriter, r *http.Request) {
297
-		s.ec[ext].applydiff++
298
-		diff := r.Body
299
-		defer r.Body.Close()
300
-
301
-		id := r.URL.Query().Get("id")
302
-		parent := r.URL.Query().Get("parent")
303
-
304
-		if id == "" {
305
-			http.Error(w, fmt.Sprintf("missing id"), 409)
306
-		}
307
-
308
-		size, err := driver.ApplyDiff(id, parent, diff)
309
-		if err != nil {
310
-			respond(w, err)
311
-			return
312
-		}
313
-		respond(w, &graphDriverResponse{Size: size})
314
-	})
315
-
316
-	mux.HandleFunc("/GraphDriver.DiffSize", func(w http.ResponseWriter, r *http.Request) {
317
-		s.ec[ext].diffsize++
318
-
319
-		var req graphDriverRequest
320
-		if err := decReq(r.Body, &req, w); err != nil {
321
-			return
322
-		}
323
-
324
-		size, err := driver.DiffSize(req.ID, req.Parent)
325
-		if err != nil {
326
-			respond(w, err)
327
-			return
328
-		}
329
-		respond(w, &graphDriverResponse{Size: size})
330
-	})
331
-
332
-	err = os.MkdirAll("/etc/docker/plugins", 0755)
333
-	c.Assert(err, check.IsNil, check.Commentf("error creating /etc/docker/plugins"))
334
-
335
-	specFile := "/etc/docker/plugins/" + name + "." + ext
336
-	err = ioutil.WriteFile(specFile, b, 0644)
337
-	c.Assert(err, check.IsNil, check.Commentf("error writing to %s", specFile))
338
-}
339
-
340
-func (s *DockerExternalGraphdriverSuite) TearDownSuite(c *check.C) {
341
-	s.server.Close()
342
-	s.jserver.Close()
343
-
344
-	err := os.RemoveAll("/etc/docker/plugins")
345
-	c.Assert(err, check.IsNil, check.Commentf("error removing /etc/docker/plugins"))
346
-}
347
-
348
-func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriver(c *check.C) {
349
-	testRequires(c, ExperimentalDaemon, SameHostDaemon)
350
-
351
-	s.testExternalGraphDriver("test-external-graph-driver", "spec", c)
352
-	s.testExternalGraphDriver("json-external-graph-driver", "json", c)
353
-}
354
-
355
-func (s *DockerExternalGraphdriverSuite) testExternalGraphDriver(name string, ext string, c *check.C) {
356
-	s.d.StartWithBusybox(c, "-s", name)
357
-
358
-	out, err := s.d.Cmd("run", "--name=graphtest", "busybox", "sh", "-c", "echo hello > /hello")
359
-	c.Assert(err, check.IsNil, check.Commentf(out))
360
-
361
-	s.d.Restart(c, "-s", name)
362
-
363
-	out, err = s.d.Cmd("inspect", "--format={{.GraphDriver.Name}}", "graphtest")
364
-	c.Assert(err, check.IsNil, check.Commentf(out))
365
-	c.Assert(strings.TrimSpace(out), check.Equals, name)
366
-
367
-	out, err = s.d.Cmd("diff", "graphtest")
368
-	c.Assert(err, check.IsNil, check.Commentf(out))
369
-	c.Assert(strings.Contains(out, "A /hello"), check.Equals, true, check.Commentf("diff output: %s", out))
370
-
371
-	out, err = s.d.Cmd("rm", "-f", "graphtest")
372
-	c.Assert(err, check.IsNil, check.Commentf(out))
373
-
374
-	out, err = s.d.Cmd("info")
375
-	c.Assert(err, check.IsNil, check.Commentf(out))
376
-
377
-	s.d.Stop(c)
378
-
379
-	// Don't check s.ec.exists, because the daemon no longer calls the
380
-	// Exists function.
381
-	c.Assert(s.ec[ext].activations, check.Equals, 2)
382
-	c.Assert(s.ec[ext].init, check.Equals, 2)
383
-	c.Assert(s.ec[ext].creations >= 1, check.Equals, true)
384
-	c.Assert(s.ec[ext].removals >= 1, check.Equals, true)
385
-	c.Assert(s.ec[ext].gets >= 1, check.Equals, true)
386
-	c.Assert(s.ec[ext].puts >= 1, check.Equals, true)
387
-	c.Assert(s.ec[ext].stats, check.Equals, 5)
388
-	c.Assert(s.ec[ext].cleanups, check.Equals, 2)
389
-	c.Assert(s.ec[ext].applydiff >= 1, check.Equals, true)
390
-	c.Assert(s.ec[ext].changes, check.Equals, 1)
391
-	c.Assert(s.ec[ext].diffsize, check.Equals, 0)
392
-	c.Assert(s.ec[ext].diff, check.Equals, 0)
393
-	c.Assert(s.ec[ext].metadata, check.Equals, 1)
394
-}
395
-
396
-func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriverPull(c *check.C) {
397
-	testRequires(c, Network, ExperimentalDaemon, SameHostDaemon)
398
-
399
-	s.d.Start(c)
400
-
401
-	out, err := s.d.Cmd("pull", "busybox:latest")
402
-	c.Assert(err, check.IsNil, check.Commentf(out))
403
-
404
-	out, err = s.d.Cmd("run", "-d", "busybox", "top")
405
-	c.Assert(err, check.IsNil, check.Commentf(out))
406
-}
... ...
@@ -5,6 +5,9 @@ import (
5 5
 	"strings"
6 6
 	"testing"
7 7
 	"time"
8
+
9
+	"github.com/docker/docker/pkg/parsers/kernel"
10
+	"github.com/gotestyourself/gotestyourself/icmd"
8 11
 )
9 12
 
10 13
 // HasHubConnectivity checks to see if https://hub.docker.com is
... ...
@@ -24,3 +27,26 @@ func HasHubConnectivity(t *testing.T) bool {
24 24
 	}
25 25
 	return err == nil
26 26
 }
27
+
28
+func overlayFSSupported() bool {
29
+	result := icmd.RunCommand("/bin/sh", "-c", "cat /proc/filesystems")
30
+	if result.Error != nil {
31
+		return false
32
+	}
33
+	return strings.Contains(result.Combined(), "overlay\n")
34
+}
35
+
36
+// Overlay2Supported returns true if the current system supports overlay2 as graphdriver
37
+func Overlay2Supported(kernelVersion string) bool {
38
+	if !overlayFSSupported() {
39
+		return false
40
+	}
41
+
42
+	daemonV, err := kernel.ParseRelease(kernelVersion)
43
+	if err != nil {
44
+		return false
45
+	}
46
+	requiredV := kernel.VersionInfo{Kernel: 4}
47
+	return kernel.CompareKernelVersion(*daemonV, requiredV) > -1
48
+
49
+}
27 50
new file mode 100644
... ...
@@ -0,0 +1,462 @@
0
+package graphdriver
1
+
2
+import (
3
+	"context"
4
+	"encoding/json"
5
+	"fmt"
6
+	"io"
7
+	"io/ioutil"
8
+	"net/http"
9
+	"net/http/httptest"
10
+	"os"
11
+	"runtime"
12
+	"testing"
13
+
14
+	"github.com/docker/docker/api/types"
15
+	containertypes "github.com/docker/docker/api/types/container"
16
+	"github.com/docker/docker/client"
17
+	"github.com/docker/docker/daemon/graphdriver"
18
+	"github.com/docker/docker/daemon/graphdriver/vfs"
19
+	"github.com/docker/docker/integration/internal/container"
20
+	"github.com/docker/docker/integration/internal/requirement"
21
+	"github.com/docker/docker/internal/test/daemon"
22
+	"github.com/docker/docker/pkg/archive"
23
+	"github.com/docker/docker/pkg/plugins"
24
+	"github.com/gotestyourself/gotestyourself/assert"
25
+	is "github.com/gotestyourself/gotestyourself/assert/cmp"
26
+	"github.com/gotestyourself/gotestyourself/skip"
27
+)
28
+
29
+type graphEventsCounter struct {
30
+	activations int
31
+	creations   int
32
+	removals    int
33
+	gets        int
34
+	puts        int
35
+	stats       int
36
+	cleanups    int
37
+	exists      int
38
+	init        int
39
+	metadata    int
40
+	diff        int
41
+	applydiff   int
42
+	changes     int
43
+	diffsize    int
44
+}
45
+
46
+func TestExternalGraphDriver(t *testing.T) {
47
+	skip.If(t, runtime.GOOS == "windows")
48
+	skip.If(t, testEnv.IsRemoteDaemon(), "cannot run daemon when remote daemon")
49
+	skip.If(t, !requirement.HasHubConnectivity(t))
50
+
51
+	// Setup plugin(s)
52
+	ec := make(map[string]*graphEventsCounter)
53
+	sserver := setupPluginViaSpecFile(t, ec)
54
+	jserver := setupPluginViaJSONFile(t, ec)
55
+	// Create daemon
56
+	d := daemon.New(t, daemon.WithExperimental)
57
+	c := d.NewClientT(t)
58
+
59
+	for _, tc := range []struct {
60
+		name string
61
+		test func(client.APIClient, *daemon.Daemon) func(*testing.T)
62
+	}{
63
+		{
64
+			name: "json",
65
+			test: testExternalGraphDriver("json", ec),
66
+		},
67
+		{
68
+			name: "spec",
69
+			test: testExternalGraphDriver("spec", ec),
70
+		},
71
+		{
72
+			name: "pull",
73
+			test: testGraphDriverPull,
74
+		},
75
+	} {
76
+		t.Run(tc.name, tc.test(c, d))
77
+	}
78
+
79
+	sserver.Close()
80
+	jserver.Close()
81
+	err := os.RemoveAll("/etc/docker/plugins")
82
+	assert.NilError(t, err)
83
+}
84
+
85
+func setupPluginViaSpecFile(t *testing.T, ec map[string]*graphEventsCounter) *httptest.Server {
86
+	mux := http.NewServeMux()
87
+	server := httptest.NewServer(mux)
88
+
89
+	setupPlugin(t, ec, "spec", mux, []byte(server.URL))
90
+
91
+	return server
92
+}
93
+
94
+func setupPluginViaJSONFile(t *testing.T, ec map[string]*graphEventsCounter) *httptest.Server {
95
+	mux := http.NewServeMux()
96
+	server := httptest.NewServer(mux)
97
+
98
+	p := plugins.NewLocalPlugin("json-external-graph-driver", server.URL)
99
+	b, err := json.Marshal(p)
100
+	assert.NilError(t, err)
101
+
102
+	setupPlugin(t, ec, "json", mux, b)
103
+
104
+	return server
105
+}
106
+
107
+func setupPlugin(t *testing.T, ec map[string]*graphEventsCounter, ext string, mux *http.ServeMux, b []byte) {
108
+	name := fmt.Sprintf("%s-external-graph-driver", ext)
109
+	type graphDriverRequest struct {
110
+		ID         string `json:",omitempty"`
111
+		Parent     string `json:",omitempty"`
112
+		MountLabel string `json:",omitempty"`
113
+		ReadOnly   bool   `json:",omitempty"`
114
+	}
115
+
116
+	type graphDriverResponse struct {
117
+		Err      error             `json:",omitempty"`
118
+		Dir      string            `json:",omitempty"`
119
+		Exists   bool              `json:",omitempty"`
120
+		Status   [][2]string       `json:",omitempty"`
121
+		Metadata map[string]string `json:",omitempty"`
122
+		Changes  []archive.Change  `json:",omitempty"`
123
+		Size     int64             `json:",omitempty"`
124
+	}
125
+
126
+	respond := func(w http.ResponseWriter, data interface{}) {
127
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
128
+		switch t := data.(type) {
129
+		case error:
130
+			fmt.Fprintln(w, fmt.Sprintf(`{"Err": %q}`, t.Error()))
131
+		case string:
132
+			fmt.Fprintln(w, t)
133
+		default:
134
+			json.NewEncoder(w).Encode(&data)
135
+		}
136
+	}
137
+
138
+	decReq := func(b io.ReadCloser, out interface{}, w http.ResponseWriter) error {
139
+		defer b.Close()
140
+		if err := json.NewDecoder(b).Decode(&out); err != nil {
141
+			http.Error(w, fmt.Sprintf("error decoding json: %s", err.Error()), 500)
142
+		}
143
+		return nil
144
+	}
145
+
146
+	base, err := ioutil.TempDir("", name)
147
+	assert.NilError(t, err)
148
+	vfsProto, err := vfs.Init(base, []string{}, nil, nil)
149
+	assert.NilError(t, err, "error initializing graph driver")
150
+	driver := graphdriver.NewNaiveDiffDriver(vfsProto, nil, nil)
151
+
152
+	ec[ext] = &graphEventsCounter{}
153
+	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
154
+		ec[ext].activations++
155
+		respond(w, `{"Implements": ["GraphDriver"]}`)
156
+	})
157
+
158
+	mux.HandleFunc("/GraphDriver.Init", func(w http.ResponseWriter, r *http.Request) {
159
+		ec[ext].init++
160
+		respond(w, "{}")
161
+	})
162
+
163
+	mux.HandleFunc("/GraphDriver.CreateReadWrite", func(w http.ResponseWriter, r *http.Request) {
164
+		ec[ext].creations++
165
+
166
+		var req graphDriverRequest
167
+		if err := decReq(r.Body, &req, w); err != nil {
168
+			return
169
+		}
170
+		if err := driver.CreateReadWrite(req.ID, req.Parent, nil); err != nil {
171
+			respond(w, err)
172
+			return
173
+		}
174
+		respond(w, "{}")
175
+	})
176
+
177
+	mux.HandleFunc("/GraphDriver.Create", func(w http.ResponseWriter, r *http.Request) {
178
+		ec[ext].creations++
179
+
180
+		var req graphDriverRequest
181
+		if err := decReq(r.Body, &req, w); err != nil {
182
+			return
183
+		}
184
+		if err := driver.Create(req.ID, req.Parent, nil); err != nil {
185
+			respond(w, err)
186
+			return
187
+		}
188
+		respond(w, "{}")
189
+	})
190
+
191
+	mux.HandleFunc("/GraphDriver.Remove", func(w http.ResponseWriter, r *http.Request) {
192
+		ec[ext].removals++
193
+
194
+		var req graphDriverRequest
195
+		if err := decReq(r.Body, &req, w); err != nil {
196
+			return
197
+		}
198
+
199
+		if err := driver.Remove(req.ID); err != nil {
200
+			respond(w, err)
201
+			return
202
+		}
203
+		respond(w, "{}")
204
+	})
205
+
206
+	mux.HandleFunc("/GraphDriver.Get", func(w http.ResponseWriter, r *http.Request) {
207
+		ec[ext].gets++
208
+
209
+		var req graphDriverRequest
210
+		if err := decReq(r.Body, &req, w); err != nil {
211
+			return
212
+		}
213
+
214
+		// TODO @gupta-ak: Figure out what to do here.
215
+		dir, err := driver.Get(req.ID, req.MountLabel)
216
+		if err != nil {
217
+			respond(w, err)
218
+			return
219
+		}
220
+		respond(w, &graphDriverResponse{Dir: dir.Path()})
221
+	})
222
+
223
+	mux.HandleFunc("/GraphDriver.Put", func(w http.ResponseWriter, r *http.Request) {
224
+		ec[ext].puts++
225
+
226
+		var req graphDriverRequest
227
+		if err := decReq(r.Body, &req, w); err != nil {
228
+			return
229
+		}
230
+
231
+		if err := driver.Put(req.ID); err != nil {
232
+			respond(w, err)
233
+			return
234
+		}
235
+		respond(w, "{}")
236
+	})
237
+
238
+	mux.HandleFunc("/GraphDriver.Exists", func(w http.ResponseWriter, r *http.Request) {
239
+		ec[ext].exists++
240
+
241
+		var req graphDriverRequest
242
+		if err := decReq(r.Body, &req, w); err != nil {
243
+			return
244
+		}
245
+		respond(w, &graphDriverResponse{Exists: driver.Exists(req.ID)})
246
+	})
247
+
248
+	mux.HandleFunc("/GraphDriver.Status", func(w http.ResponseWriter, r *http.Request) {
249
+		ec[ext].stats++
250
+		respond(w, &graphDriverResponse{Status: driver.Status()})
251
+	})
252
+
253
+	mux.HandleFunc("/GraphDriver.Cleanup", func(w http.ResponseWriter, r *http.Request) {
254
+		ec[ext].cleanups++
255
+		err := driver.Cleanup()
256
+		if err != nil {
257
+			respond(w, err)
258
+			return
259
+		}
260
+		respond(w, `{}`)
261
+	})
262
+
263
+	mux.HandleFunc("/GraphDriver.GetMetadata", func(w http.ResponseWriter, r *http.Request) {
264
+		ec[ext].metadata++
265
+
266
+		var req graphDriverRequest
267
+		if err := decReq(r.Body, &req, w); err != nil {
268
+			return
269
+		}
270
+
271
+		data, err := driver.GetMetadata(req.ID)
272
+		if err != nil {
273
+			respond(w, err)
274
+			return
275
+		}
276
+		respond(w, &graphDriverResponse{Metadata: data})
277
+	})
278
+
279
+	mux.HandleFunc("/GraphDriver.Diff", func(w http.ResponseWriter, r *http.Request) {
280
+		ec[ext].diff++
281
+
282
+		var req graphDriverRequest
283
+		if err := decReq(r.Body, &req, w); err != nil {
284
+			return
285
+		}
286
+
287
+		diff, err := driver.Diff(req.ID, req.Parent)
288
+		if err != nil {
289
+			respond(w, err)
290
+			return
291
+		}
292
+		io.Copy(w, diff)
293
+	})
294
+
295
+	mux.HandleFunc("/GraphDriver.Changes", func(w http.ResponseWriter, r *http.Request) {
296
+		ec[ext].changes++
297
+		var req graphDriverRequest
298
+		if err := decReq(r.Body, &req, w); err != nil {
299
+			return
300
+		}
301
+
302
+		changes, err := driver.Changes(req.ID, req.Parent)
303
+		if err != nil {
304
+			respond(w, err)
305
+			return
306
+		}
307
+		respond(w, &graphDriverResponse{Changes: changes})
308
+	})
309
+
310
+	mux.HandleFunc("/GraphDriver.ApplyDiff", func(w http.ResponseWriter, r *http.Request) {
311
+		ec[ext].applydiff++
312
+		diff := r.Body
313
+		defer r.Body.Close()
314
+
315
+		id := r.URL.Query().Get("id")
316
+		parent := r.URL.Query().Get("parent")
317
+
318
+		if id == "" {
319
+			http.Error(w, fmt.Sprintf("missing id"), 409)
320
+		}
321
+
322
+		size, err := driver.ApplyDiff(id, parent, diff)
323
+		if err != nil {
324
+			respond(w, err)
325
+			return
326
+		}
327
+		respond(w, &graphDriverResponse{Size: size})
328
+	})
329
+
330
+	mux.HandleFunc("/GraphDriver.DiffSize", func(w http.ResponseWriter, r *http.Request) {
331
+		ec[ext].diffsize++
332
+
333
+		var req graphDriverRequest
334
+		if err := decReq(r.Body, &req, w); err != nil {
335
+			return
336
+		}
337
+
338
+		size, err := driver.DiffSize(req.ID, req.Parent)
339
+		if err != nil {
340
+			respond(w, err)
341
+			return
342
+		}
343
+		respond(w, &graphDriverResponse{Size: size})
344
+	})
345
+
346
+	err = os.MkdirAll("/etc/docker/plugins", 0755)
347
+	assert.NilError(t, err)
348
+
349
+	specFile := "/etc/docker/plugins/" + name + "." + ext
350
+	err = ioutil.WriteFile(specFile, b, 0644)
351
+	assert.NilError(t, err)
352
+}
353
+
354
+func testExternalGraphDriver(ext string, ec map[string]*graphEventsCounter) func(client.APIClient, *daemon.Daemon) func(*testing.T) {
355
+	return func(c client.APIClient, d *daemon.Daemon) func(*testing.T) {
356
+		return func(t *testing.T) {
357
+			driverName := fmt.Sprintf("%s-external-graph-driver", ext)
358
+			d.StartWithBusybox(t, "-s", driverName)
359
+
360
+			ctx := context.Background()
361
+
362
+			testGraphDriver(t, c, ctx, driverName, func(t *testing.T) {
363
+				d.Restart(t, "-s", driverName)
364
+			})
365
+
366
+			_, err := c.Info(ctx)
367
+			assert.NilError(t, err)
368
+
369
+			d.Stop(t)
370
+
371
+			// Don't check ec.exists, because the daemon no longer calls the
372
+			// Exists function.
373
+			assert.Check(t, is.Equal(ec[ext].activations, 2))
374
+			assert.Check(t, is.Equal(ec[ext].init, 2))
375
+			assert.Check(t, ec[ext].creations >= 1)
376
+			assert.Check(t, ec[ext].removals >= 1)
377
+			assert.Check(t, ec[ext].gets >= 1)
378
+			assert.Check(t, ec[ext].puts >= 1)
379
+			assert.Check(t, is.Equal(ec[ext].stats, 5))
380
+			assert.Check(t, is.Equal(ec[ext].cleanups, 2))
381
+			assert.Check(t, ec[ext].applydiff >= 1)
382
+			assert.Check(t, is.Equal(ec[ext].changes, 1))
383
+			assert.Check(t, is.Equal(ec[ext].diffsize, 0))
384
+			assert.Check(t, is.Equal(ec[ext].diff, 0))
385
+			assert.Check(t, is.Equal(ec[ext].metadata, 1))
386
+		}
387
+	}
388
+}
389
+
390
+func testGraphDriverPull(c client.APIClient, d *daemon.Daemon) func(*testing.T) {
391
+	return func(t *testing.T) {
392
+		d.Start(t)
393
+		defer d.Stop(t)
394
+		ctx := context.Background()
395
+
396
+		r, err := c.ImagePull(ctx, "busybox:latest", types.ImagePullOptions{})
397
+		assert.NilError(t, err)
398
+		_, err = io.Copy(ioutil.Discard, r)
399
+		assert.NilError(t, err)
400
+
401
+		container.Run(t, ctx, c, container.WithImage("busybox:latest"))
402
+	}
403
+}
404
+
405
+func TestGraphdriverPluginV2(t *testing.T) {
406
+	skip.If(t, runtime.GOOS == "windows")
407
+	skip.If(t, testEnv.IsRemoteDaemon(), "cannot run daemon when remote daemon")
408
+	skip.If(t, !requirement.HasHubConnectivity(t))
409
+	skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
410
+	skip.If(t, !requirement.Overlay2Supported(testEnv.DaemonInfo.KernelVersion))
411
+
412
+	d := daemon.New(t, daemon.WithExperimental)
413
+	d.Start(t)
414
+	defer d.Stop(t)
415
+
416
+	client := d.NewClientT(t)
417
+	defer client.Close()
418
+	ctx := context.Background()
419
+
420
+	// install the plugin
421
+	plugin := "cpuguy83/docker-overlay2-graphdriver-plugin"
422
+	responseReader, err := client.PluginInstall(ctx, plugin, types.PluginInstallOptions{
423
+		RemoteRef:            plugin,
424
+		AcceptAllPermissions: true,
425
+	})
426
+	defer responseReader.Close()
427
+	assert.NilError(t, err)
428
+	// ensure it's done by waiting for EOF on the response
429
+	_, err = io.Copy(ioutil.Discard, responseReader)
430
+	assert.NilError(t, err)
431
+
432
+	// restart the daemon with the plugin set as the storage driver
433
+	d.Stop(t)
434
+	d.StartWithBusybox(t, "-s", plugin, "--storage-opt", "overlay2.override_kernel_check=1")
435
+
436
+	testGraphDriver(t, client, ctx, plugin, nil)
437
+}
438
+
439
+func testGraphDriver(t *testing.T, c client.APIClient, ctx context.Context, driverName string, afterContainerRunFn func(*testing.T)) { //nolint: golint
440
+	id := container.Run(t, ctx, c, container.WithCmd("sh", "-c", "echo hello > /hello"))
441
+
442
+	if afterContainerRunFn != nil {
443
+		afterContainerRunFn(t)
444
+	}
445
+
446
+	i, err := c.ContainerInspect(ctx, id)
447
+	assert.NilError(t, err)
448
+	assert.Check(t, is.Equal(i.GraphDriver.Name, driverName))
449
+
450
+	diffs, err := c.ContainerDiff(ctx, id)
451
+	assert.NilError(t, err)
452
+	assert.Check(t, is.Contains(diffs, containertypes.ContainerChangeResponseItem{
453
+		Kind: archive.ChangeAdd,
454
+		Path: "/hello",
455
+	}), "diffs: %v", diffs)
456
+
457
+	err = c.ContainerRemove(ctx, id, types.ContainerRemoveOptions{
458
+		Force: true,
459
+	})
460
+	assert.NilError(t, err)
461
+}
0 462
new file mode 100644
... ...
@@ -0,0 +1,36 @@
0
+package graphdriver // import "github.com/docker/docker/integration/plugin/graphdriver"
1
+
2
+import (
3
+	"fmt"
4
+	"os"
5
+	"testing"
6
+
7
+	"github.com/docker/docker/internal/test/environment"
8
+	"github.com/docker/docker/pkg/reexec"
9
+)
10
+
11
+var (
12
+	testEnv *environment.Execution
13
+)
14
+
15
+func init() {
16
+	reexec.Init() // This is required for external graphdriver tests
17
+}
18
+
19
+const dockerdBinary = "dockerd"
20
+
21
+func TestMain(m *testing.M) {
22
+	var err error
23
+	testEnv, err = environment.New()
24
+	if err != nil {
25
+		fmt.Println(err)
26
+		os.Exit(1)
27
+	}
28
+	err = environment.EnsureFrozenImagesLinux(testEnv)
29
+	if err != nil {
30
+		fmt.Println(err)
31
+		os.Exit(1)
32
+	}
33
+	testEnv.Print()
34
+	os.Exit(m.Run())
35
+}