github.com/cookieai-jar/moby@v17.12.1-ce-rc2+incompatible/integration-cli/docker_cli_external_graphdriver_unix_test.go (about) 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 "github.com/docker/docker/pkg/archive" 19 "github.com/docker/docker/pkg/plugins" 20 "github.com/go-check/check" 21 ) 22 23 func init() { 24 check.Suite(&DockerExternalGraphdriverSuite{ 25 ds: &DockerSuite{}, 26 }) 27 } 28 29 type DockerExternalGraphdriverSuite struct { 30 server *httptest.Server 31 jserver *httptest.Server 32 ds *DockerSuite 33 d *daemon.Daemon 34 ec map[string]*graphEventsCounter 35 } 36 37 type graphEventsCounter struct { 38 activations int 39 creations int 40 removals int 41 gets int 42 puts int 43 stats int 44 cleanups int 45 exists int 46 init int 47 metadata int 48 diff int 49 applydiff int 50 changes int 51 diffsize int 52 } 53 54 func (s *DockerExternalGraphdriverSuite) SetUpTest(c *check.C) { 55 s.d = daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{ 56 Experimental: testEnv.ExperimentalDaemon(), 57 }) 58 } 59 60 func (s *DockerExternalGraphdriverSuite) OnTimeout(c *check.C) { 61 s.d.DumpStackAndQuit() 62 } 63 64 func (s *DockerExternalGraphdriverSuite) TearDownTest(c *check.C) { 65 if s.d != nil { 66 s.d.Stop(c) 67 s.ds.TearDownTest(c) 68 } 69 } 70 71 func (s *DockerExternalGraphdriverSuite) SetUpSuite(c *check.C) { 72 s.ec = make(map[string]*graphEventsCounter) 73 s.setUpPluginViaSpecFile(c) 74 s.setUpPluginViaJSONFile(c) 75 } 76 77 func (s *DockerExternalGraphdriverSuite) setUpPluginViaSpecFile(c *check.C) { 78 mux := http.NewServeMux() 79 s.server = httptest.NewServer(mux) 80 81 s.setUpPlugin(c, "test-external-graph-driver", "spec", mux, []byte(s.server.URL)) 82 } 83 84 func (s *DockerExternalGraphdriverSuite) setUpPluginViaJSONFile(c *check.C) { 85 mux := http.NewServeMux() 86 s.jserver = httptest.NewServer(mux) 87 88 p := plugins.NewLocalPlugin("json-external-graph-driver", s.jserver.URL) 89 b, err := json.Marshal(p) 90 c.Assert(err, check.IsNil) 91 92 s.setUpPlugin(c, "json-external-graph-driver", "json", mux, b) 93 } 94 95 func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ext string, mux *http.ServeMux, b []byte) { 96 type graphDriverRequest struct { 97 ID string `json:",omitempty"` 98 Parent string `json:",omitempty"` 99 MountLabel string `json:",omitempty"` 100 ReadOnly bool `json:",omitempty"` 101 } 102 103 type graphDriverResponse struct { 104 Err error `json:",omitempty"` 105 Dir string `json:",omitempty"` 106 Exists bool `json:",omitempty"` 107 Status [][2]string `json:",omitempty"` 108 Metadata map[string]string `json:",omitempty"` 109 Changes []archive.Change `json:",omitempty"` 110 Size int64 `json:",omitempty"` 111 } 112 113 respond := func(w http.ResponseWriter, data interface{}) { 114 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 115 switch t := data.(type) { 116 case error: 117 fmt.Fprintln(w, fmt.Sprintf(`{"Err": %q}`, t.Error())) 118 case string: 119 fmt.Fprintln(w, t) 120 default: 121 json.NewEncoder(w).Encode(&data) 122 } 123 } 124 125 decReq := func(b io.ReadCloser, out interface{}, w http.ResponseWriter) error { 126 defer b.Close() 127 if err := json.NewDecoder(b).Decode(&out); err != nil { 128 http.Error(w, fmt.Sprintf("error decoding json: %s", err.Error()), 500) 129 } 130 return nil 131 } 132 133 base, err := ioutil.TempDir("", name) 134 c.Assert(err, check.IsNil) 135 vfsProto, err := vfs.Init(base, []string{}, nil, nil) 136 c.Assert(err, check.IsNil, check.Commentf("error initializing graph driver")) 137 driver := graphdriver.NewNaiveDiffDriver(vfsProto, nil, nil) 138 139 s.ec[ext] = &graphEventsCounter{} 140 mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { 141 s.ec[ext].activations++ 142 respond(w, `{"Implements": ["GraphDriver"]}`) 143 }) 144 145 mux.HandleFunc("/GraphDriver.Init", func(w http.ResponseWriter, r *http.Request) { 146 s.ec[ext].init++ 147 respond(w, "{}") 148 }) 149 150 mux.HandleFunc("/GraphDriver.CreateReadWrite", func(w http.ResponseWriter, r *http.Request) { 151 s.ec[ext].creations++ 152 153 var req graphDriverRequest 154 if err := decReq(r.Body, &req, w); err != nil { 155 return 156 } 157 if err := driver.CreateReadWrite(req.ID, req.Parent, nil); err != nil { 158 respond(w, err) 159 return 160 } 161 respond(w, "{}") 162 }) 163 164 mux.HandleFunc("/GraphDriver.Create", func(w http.ResponseWriter, r *http.Request) { 165 s.ec[ext].creations++ 166 167 var req graphDriverRequest 168 if err := decReq(r.Body, &req, w); err != nil { 169 return 170 } 171 if err := driver.Create(req.ID, req.Parent, nil); err != nil { 172 respond(w, err) 173 return 174 } 175 respond(w, "{}") 176 }) 177 178 mux.HandleFunc("/GraphDriver.Remove", func(w http.ResponseWriter, r *http.Request) { 179 s.ec[ext].removals++ 180 181 var req graphDriverRequest 182 if err := decReq(r.Body, &req, w); err != nil { 183 return 184 } 185 186 if err := driver.Remove(req.ID); err != nil { 187 respond(w, err) 188 return 189 } 190 respond(w, "{}") 191 }) 192 193 mux.HandleFunc("/GraphDriver.Get", func(w http.ResponseWriter, r *http.Request) { 194 s.ec[ext].gets++ 195 196 var req graphDriverRequest 197 if err := decReq(r.Body, &req, w); err != nil { 198 return 199 } 200 201 // TODO @gupta-ak: Figure out what to do here. 202 dir, err := driver.Get(req.ID, req.MountLabel) 203 if err != nil { 204 respond(w, err) 205 return 206 } 207 respond(w, &graphDriverResponse{Dir: dir.Path()}) 208 }) 209 210 mux.HandleFunc("/GraphDriver.Put", func(w http.ResponseWriter, r *http.Request) { 211 s.ec[ext].puts++ 212 213 var req graphDriverRequest 214 if err := decReq(r.Body, &req, w); err != nil { 215 return 216 } 217 218 if err := driver.Put(req.ID); err != nil { 219 respond(w, err) 220 return 221 } 222 respond(w, "{}") 223 }) 224 225 mux.HandleFunc("/GraphDriver.Exists", func(w http.ResponseWriter, r *http.Request) { 226 s.ec[ext].exists++ 227 228 var req graphDriverRequest 229 if err := decReq(r.Body, &req, w); err != nil { 230 return 231 } 232 respond(w, &graphDriverResponse{Exists: driver.Exists(req.ID)}) 233 }) 234 235 mux.HandleFunc("/GraphDriver.Status", func(w http.ResponseWriter, r *http.Request) { 236 s.ec[ext].stats++ 237 respond(w, &graphDriverResponse{Status: driver.Status()}) 238 }) 239 240 mux.HandleFunc("/GraphDriver.Cleanup", func(w http.ResponseWriter, r *http.Request) { 241 s.ec[ext].cleanups++ 242 err := driver.Cleanup() 243 if err != nil { 244 respond(w, err) 245 return 246 } 247 respond(w, `{}`) 248 }) 249 250 mux.HandleFunc("/GraphDriver.GetMetadata", func(w http.ResponseWriter, r *http.Request) { 251 s.ec[ext].metadata++ 252 253 var req graphDriverRequest 254 if err := decReq(r.Body, &req, w); err != nil { 255 return 256 } 257 258 data, err := driver.GetMetadata(req.ID) 259 if err != nil { 260 respond(w, err) 261 return 262 } 263 respond(w, &graphDriverResponse{Metadata: data}) 264 }) 265 266 mux.HandleFunc("/GraphDriver.Diff", func(w http.ResponseWriter, r *http.Request) { 267 s.ec[ext].diff++ 268 269 var req graphDriverRequest 270 if err := decReq(r.Body, &req, w); err != nil { 271 return 272 } 273 274 diff, err := driver.Diff(req.ID, req.Parent) 275 if err != nil { 276 respond(w, err) 277 return 278 } 279 io.Copy(w, diff) 280 }) 281 282 mux.HandleFunc("/GraphDriver.Changes", func(w http.ResponseWriter, r *http.Request) { 283 s.ec[ext].changes++ 284 var req graphDriverRequest 285 if err := decReq(r.Body, &req, w); err != nil { 286 return 287 } 288 289 changes, err := driver.Changes(req.ID, req.Parent) 290 if err != nil { 291 respond(w, err) 292 return 293 } 294 respond(w, &graphDriverResponse{Changes: changes}) 295 }) 296 297 mux.HandleFunc("/GraphDriver.ApplyDiff", func(w http.ResponseWriter, r *http.Request) { 298 s.ec[ext].applydiff++ 299 diff := r.Body 300 defer r.Body.Close() 301 302 id := r.URL.Query().Get("id") 303 parent := r.URL.Query().Get("parent") 304 305 if id == "" { 306 http.Error(w, fmt.Sprintf("missing id"), 409) 307 } 308 309 size, err := driver.ApplyDiff(id, parent, diff) 310 if err != nil { 311 respond(w, err) 312 return 313 } 314 respond(w, &graphDriverResponse{Size: size}) 315 }) 316 317 mux.HandleFunc("/GraphDriver.DiffSize", func(w http.ResponseWriter, r *http.Request) { 318 s.ec[ext].diffsize++ 319 320 var req graphDriverRequest 321 if err := decReq(r.Body, &req, w); err != nil { 322 return 323 } 324 325 size, err := driver.DiffSize(req.ID, req.Parent) 326 if err != nil { 327 respond(w, err) 328 return 329 } 330 respond(w, &graphDriverResponse{Size: size}) 331 }) 332 333 err = os.MkdirAll("/etc/docker/plugins", 0755) 334 c.Assert(err, check.IsNil, check.Commentf("error creating /etc/docker/plugins")) 335 336 specFile := "/etc/docker/plugins/" + name + "." + ext 337 err = ioutil.WriteFile(specFile, b, 0644) 338 c.Assert(err, check.IsNil, check.Commentf("error writing to %s", specFile)) 339 } 340 341 func (s *DockerExternalGraphdriverSuite) TearDownSuite(c *check.C) { 342 s.server.Close() 343 s.jserver.Close() 344 345 err := os.RemoveAll("/etc/docker/plugins") 346 c.Assert(err, check.IsNil, check.Commentf("error removing /etc/docker/plugins")) 347 } 348 349 func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriver(c *check.C) { 350 testRequires(c, ExperimentalDaemon) 351 352 s.testExternalGraphDriver("test-external-graph-driver", "spec", c) 353 s.testExternalGraphDriver("json-external-graph-driver", "json", c) 354 } 355 356 func (s *DockerExternalGraphdriverSuite) testExternalGraphDriver(name string, ext string, c *check.C) { 357 s.d.StartWithBusybox(c, "-s", name) 358 359 out, err := s.d.Cmd("run", "--name=graphtest", "busybox", "sh", "-c", "echo hello > /hello") 360 c.Assert(err, check.IsNil, check.Commentf(out)) 361 362 s.d.Restart(c, "-s", name) 363 364 out, err = s.d.Cmd("inspect", "--format={{.GraphDriver.Name}}", "graphtest") 365 c.Assert(err, check.IsNil, check.Commentf(out)) 366 c.Assert(strings.TrimSpace(out), check.Equals, name) 367 368 out, err = s.d.Cmd("diff", "graphtest") 369 c.Assert(err, check.IsNil, check.Commentf(out)) 370 c.Assert(strings.Contains(out, "A /hello"), check.Equals, true, check.Commentf("diff output: %s", out)) 371 372 out, err = s.d.Cmd("rm", "-f", "graphtest") 373 c.Assert(err, check.IsNil, check.Commentf(out)) 374 375 out, err = s.d.Cmd("info") 376 c.Assert(err, check.IsNil, check.Commentf(out)) 377 378 s.d.Stop(c) 379 380 // Don't check s.ec.exists, because the daemon no longer calls the 381 // Exists function. 382 c.Assert(s.ec[ext].activations, check.Equals, 2) 383 c.Assert(s.ec[ext].init, check.Equals, 2) 384 c.Assert(s.ec[ext].creations >= 1, check.Equals, true) 385 c.Assert(s.ec[ext].removals >= 1, check.Equals, true) 386 c.Assert(s.ec[ext].gets >= 1, check.Equals, true) 387 c.Assert(s.ec[ext].puts >= 1, check.Equals, true) 388 c.Assert(s.ec[ext].stats, check.Equals, 5) 389 c.Assert(s.ec[ext].cleanups, check.Equals, 2) 390 c.Assert(s.ec[ext].applydiff >= 1, check.Equals, true) 391 c.Assert(s.ec[ext].changes, check.Equals, 1) 392 c.Assert(s.ec[ext].diffsize, check.Equals, 0) 393 c.Assert(s.ec[ext].diff, check.Equals, 0) 394 c.Assert(s.ec[ext].metadata, check.Equals, 1) 395 } 396 397 func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriverPull(c *check.C) { 398 testRequires(c, Network, ExperimentalDaemon) 399 400 s.d.Start(c) 401 402 out, err := s.d.Cmd("pull", "busybox:latest") 403 c.Assert(err, check.IsNil, check.Commentf(out)) 404 405 out, err = s.d.Cmd("run", "-d", "busybox", "top") 406 c.Assert(err, check.IsNil, check.Commentf(out)) 407 }