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