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