github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/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", "appplication/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 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}) 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) 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) 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 }