github.com/ruphin/docker@v1.10.1/integration-cli/docker_cli_start_volume_driver_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 "os/exec" 14 "path/filepath" 15 "strings" 16 "time" 17 18 "github.com/docker/docker/pkg/integration/checker" 19 "github.com/go-check/check" 20 ) 21 22 func init() { 23 check.Suite(&DockerExternalVolumeSuite{ 24 ds: &DockerSuite{}, 25 }) 26 } 27 28 type eventCounter struct { 29 activations int 30 creations int 31 removals int 32 mounts int 33 unmounts int 34 paths int 35 lists int 36 gets int 37 } 38 39 type DockerExternalVolumeSuite struct { 40 server *httptest.Server 41 ds *DockerSuite 42 d *Daemon 43 ec *eventCounter 44 } 45 46 func (s *DockerExternalVolumeSuite) SetUpTest(c *check.C) { 47 s.d = NewDaemon(c) 48 s.ec = &eventCounter{} 49 } 50 51 func (s *DockerExternalVolumeSuite) TearDownTest(c *check.C) { 52 s.d.Stop() 53 s.ds.TearDownTest(c) 54 } 55 56 func (s *DockerExternalVolumeSuite) SetUpSuite(c *check.C) { 57 mux := http.NewServeMux() 58 s.server = httptest.NewServer(mux) 59 60 type pluginRequest struct { 61 Name string 62 } 63 64 type pluginResp struct { 65 Mountpoint string `json:",omitempty"` 66 Err string `json:",omitempty"` 67 } 68 69 type vol struct { 70 Name string 71 Mountpoint string 72 } 73 var volList []vol 74 75 read := func(b io.ReadCloser) (pluginRequest, error) { 76 defer b.Close() 77 var pr pluginRequest 78 if err := json.NewDecoder(b).Decode(&pr); err != nil { 79 return pr, err 80 } 81 return pr, nil 82 } 83 84 send := func(w http.ResponseWriter, data interface{}) { 85 switch t := data.(type) { 86 case error: 87 http.Error(w, t.Error(), 500) 88 case string: 89 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 90 fmt.Fprintln(w, t) 91 default: 92 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 93 json.NewEncoder(w).Encode(&data) 94 } 95 } 96 97 mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { 98 s.ec.activations++ 99 send(w, `{"Implements": ["VolumeDriver"]}`) 100 }) 101 102 mux.HandleFunc("/VolumeDriver.Create", func(w http.ResponseWriter, r *http.Request) { 103 s.ec.creations++ 104 pr, err := read(r.Body) 105 if err != nil { 106 send(w, err) 107 return 108 } 109 volList = append(volList, vol{Name: pr.Name}) 110 send(w, nil) 111 }) 112 113 mux.HandleFunc("/VolumeDriver.List", func(w http.ResponseWriter, r *http.Request) { 114 s.ec.lists++ 115 send(w, map[string][]vol{"Volumes": volList}) 116 }) 117 118 mux.HandleFunc("/VolumeDriver.Get", func(w http.ResponseWriter, r *http.Request) { 119 s.ec.gets++ 120 pr, err := read(r.Body) 121 if err != nil { 122 send(w, err) 123 return 124 } 125 126 for _, v := range volList { 127 if v.Name == pr.Name { 128 v.Mountpoint = hostVolumePath(pr.Name) 129 send(w, map[string]vol{"Volume": v}) 130 return 131 } 132 } 133 send(w, `{"Err": "no such volume"}`) 134 }) 135 136 mux.HandleFunc("/VolumeDriver.Remove", func(w http.ResponseWriter, r *http.Request) { 137 s.ec.removals++ 138 pr, err := read(r.Body) 139 if err != nil { 140 send(w, err) 141 return 142 } 143 144 if err := os.RemoveAll(hostVolumePath(pr.Name)); err != nil { 145 send(w, &pluginResp{Err: err.Error()}) 146 return 147 } 148 149 for i, v := range volList { 150 if v.Name == pr.Name { 151 if err := os.RemoveAll(hostVolumePath(v.Name)); err != nil { 152 send(w, fmt.Sprintf(`{"Err": "%v"}`, err)) 153 return 154 } 155 volList = append(volList[:i], volList[i+1:]...) 156 break 157 } 158 } 159 send(w, nil) 160 }) 161 162 mux.HandleFunc("/VolumeDriver.Path", func(w http.ResponseWriter, r *http.Request) { 163 s.ec.paths++ 164 165 pr, err := read(r.Body) 166 if err != nil { 167 send(w, err) 168 return 169 } 170 p := hostVolumePath(pr.Name) 171 send(w, &pluginResp{Mountpoint: p}) 172 }) 173 174 mux.HandleFunc("/VolumeDriver.Mount", func(w http.ResponseWriter, r *http.Request) { 175 s.ec.mounts++ 176 177 pr, err := read(r.Body) 178 if err != nil { 179 send(w, err) 180 return 181 } 182 183 p := hostVolumePath(pr.Name) 184 if err := os.MkdirAll(p, 0755); err != nil { 185 send(w, &pluginResp{Err: err.Error()}) 186 return 187 } 188 189 if err := ioutil.WriteFile(filepath.Join(p, "test"), []byte(s.server.URL), 0644); err != nil { 190 send(w, err) 191 return 192 } 193 194 send(w, &pluginResp{Mountpoint: p}) 195 }) 196 197 mux.HandleFunc("/VolumeDriver.Unmount", func(w http.ResponseWriter, r *http.Request) { 198 s.ec.unmounts++ 199 200 _, err := read(r.Body) 201 if err != nil { 202 send(w, err) 203 return 204 } 205 206 send(w, nil) 207 }) 208 209 err := os.MkdirAll("/etc/docker/plugins", 0755) 210 c.Assert(err, checker.IsNil) 211 212 err = ioutil.WriteFile("/etc/docker/plugins/test-external-volume-driver.spec", []byte(s.server.URL), 0644) 213 c.Assert(err, checker.IsNil) 214 } 215 216 func (s *DockerExternalVolumeSuite) TearDownSuite(c *check.C) { 217 s.server.Close() 218 219 err := os.RemoveAll("/etc/docker/plugins") 220 c.Assert(err, checker.IsNil) 221 } 222 223 func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverNamed(c *check.C) { 224 err := s.d.StartWithBusybox() 225 c.Assert(err, checker.IsNil) 226 227 out, err := s.d.Cmd("run", "--rm", "--name", "test-data", "-v", "external-volume-test:/tmp/external-volume-test", "--volume-driver", "test-external-volume-driver", "busybox:latest", "cat", "/tmp/external-volume-test/test") 228 c.Assert(err, checker.IsNil, check.Commentf(out)) 229 c.Assert(out, checker.Contains, s.server.URL) 230 231 _, err = s.d.Cmd("volume", "rm", "external-volume-test") 232 c.Assert(err, checker.IsNil) 233 234 p := hostVolumePath("external-volume-test") 235 _, err = os.Lstat(p) 236 c.Assert(err, checker.NotNil) 237 c.Assert(os.IsNotExist(err), checker.True, check.Commentf("Expected volume path in host to not exist: %s, %v\n", p, err)) 238 239 c.Assert(s.ec.activations, checker.Equals, 1) 240 c.Assert(s.ec.creations, checker.Equals, 1) 241 c.Assert(s.ec.removals, checker.Equals, 1) 242 c.Assert(s.ec.mounts, checker.Equals, 1) 243 c.Assert(s.ec.unmounts, checker.Equals, 1) 244 } 245 246 func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverUnnamed(c *check.C) { 247 err := s.d.StartWithBusybox() 248 c.Assert(err, checker.IsNil) 249 250 out, err := s.d.Cmd("run", "--rm", "--name", "test-data", "-v", "/tmp/external-volume-test", "--volume-driver", "test-external-volume-driver", "busybox:latest", "cat", "/tmp/external-volume-test/test") 251 c.Assert(err, checker.IsNil, check.Commentf(out)) 252 c.Assert(out, checker.Contains, s.server.URL) 253 254 c.Assert(s.ec.activations, checker.Equals, 1) 255 c.Assert(s.ec.creations, checker.Equals, 1) 256 c.Assert(s.ec.removals, checker.Equals, 1) 257 c.Assert(s.ec.mounts, checker.Equals, 1) 258 c.Assert(s.ec.unmounts, checker.Equals, 1) 259 } 260 261 func (s DockerExternalVolumeSuite) TestExternalVolumeDriverVolumesFrom(c *check.C) { 262 err := s.d.StartWithBusybox() 263 c.Assert(err, checker.IsNil) 264 265 out, err := s.d.Cmd("run", "-d", "--name", "vol-test1", "-v", "/foo", "--volume-driver", "test-external-volume-driver", "busybox:latest") 266 c.Assert(err, checker.IsNil, check.Commentf(out)) 267 268 out, err = s.d.Cmd("run", "--rm", "--volumes-from", "vol-test1", "--name", "vol-test2", "busybox", "ls", "/tmp") 269 c.Assert(err, checker.IsNil, check.Commentf(out)) 270 271 out, err = s.d.Cmd("rm", "-fv", "vol-test1") 272 c.Assert(err, checker.IsNil, check.Commentf(out)) 273 274 c.Assert(s.ec.activations, checker.Equals, 1) 275 c.Assert(s.ec.creations, checker.Equals, 1) 276 c.Assert(s.ec.removals, checker.Equals, 1) 277 c.Assert(s.ec.mounts, checker.Equals, 2) 278 c.Assert(s.ec.unmounts, checker.Equals, 2) 279 } 280 281 func (s DockerExternalVolumeSuite) TestExternalVolumeDriverDeleteContainer(c *check.C) { 282 err := s.d.StartWithBusybox() 283 c.Assert(err, checker.IsNil) 284 285 out, err := s.d.Cmd("run", "-d", "--name", "vol-test1", "-v", "/foo", "--volume-driver", "test-external-volume-driver", "busybox:latest") 286 c.Assert(err, checker.IsNil, check.Commentf(out)) 287 288 out, err = s.d.Cmd("rm", "-fv", "vol-test1") 289 c.Assert(err, checker.IsNil, check.Commentf(out)) 290 291 c.Assert(s.ec.activations, checker.Equals, 1) 292 c.Assert(s.ec.creations, checker.Equals, 1) 293 c.Assert(s.ec.removals, checker.Equals, 1) 294 c.Assert(s.ec.mounts, checker.Equals, 1) 295 c.Assert(s.ec.unmounts, checker.Equals, 1) 296 } 297 298 func hostVolumePath(name string) string { 299 return fmt.Sprintf("/var/lib/docker/volumes/%s", name) 300 } 301 302 // Make sure a request to use a down driver doesn't block other requests 303 func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverLookupNotBlocked(c *check.C) { 304 specPath := "/etc/docker/plugins/down-driver.spec" 305 err := ioutil.WriteFile(specPath, []byte("tcp://127.0.0.7:9999"), 0644) 306 c.Assert(err, check.IsNil) 307 defer os.RemoveAll(specPath) 308 309 chCmd1 := make(chan struct{}) 310 chCmd2 := make(chan error) 311 cmd1 := exec.Command(dockerBinary, "volume", "create", "-d", "down-driver") 312 cmd2 := exec.Command(dockerBinary, "volume", "create") 313 314 c.Assert(cmd1.Start(), checker.IsNil) 315 defer cmd1.Process.Kill() 316 time.Sleep(100 * time.Millisecond) // ensure API has been called 317 c.Assert(cmd2.Start(), checker.IsNil) 318 319 go func() { 320 cmd1.Wait() 321 close(chCmd1) 322 }() 323 go func() { 324 chCmd2 <- cmd2.Wait() 325 }() 326 327 select { 328 case <-chCmd1: 329 cmd2.Process.Kill() 330 c.Fatalf("volume create with down driver finished unexpectedly") 331 case err := <-chCmd2: 332 c.Assert(err, checker.IsNil) 333 case <-time.After(5 * time.Second): 334 cmd2.Process.Kill() 335 c.Fatal("volume creates are blocked by previous create requests when previous driver is down") 336 } 337 } 338 339 func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverRetryNotImmediatelyExists(c *check.C) { 340 err := s.d.StartWithBusybox() 341 c.Assert(err, checker.IsNil) 342 343 specPath := "/etc/docker/plugins/test-external-volume-driver-retry.spec" 344 os.RemoveAll(specPath) 345 defer os.RemoveAll(specPath) 346 347 errchan := make(chan error) 348 go func() { 349 if out, err := s.d.Cmd("run", "--rm", "--name", "test-data-retry", "-v", "external-volume-test:/tmp/external-volume-test", "--volume-driver", "test-external-volume-driver-retry", "busybox:latest"); err != nil { 350 errchan <- fmt.Errorf("%v:\n%s", err, out) 351 } 352 close(errchan) 353 }() 354 go func() { 355 // wait for a retry to occur, then create spec to allow plugin to register 356 time.Sleep(2000 * time.Millisecond) 357 // no need to check for an error here since it will get picked up by the timeout later 358 ioutil.WriteFile(specPath, []byte(s.server.URL), 0644) 359 }() 360 361 select { 362 case err := <-errchan: 363 c.Assert(err, checker.IsNil) 364 case <-time.After(8 * time.Second): 365 c.Fatal("volume creates fail when plugin not immediately available") 366 } 367 368 _, err = s.d.Cmd("volume", "rm", "external-volume-test") 369 c.Assert(err, checker.IsNil) 370 371 c.Assert(s.ec.activations, checker.Equals, 1) 372 c.Assert(s.ec.creations, checker.Equals, 1) 373 c.Assert(s.ec.removals, checker.Equals, 1) 374 c.Assert(s.ec.mounts, checker.Equals, 1) 375 c.Assert(s.ec.unmounts, checker.Equals, 1) 376 } 377 378 func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverBindExternalVolume(c *check.C) { 379 dockerCmd(c, "volume", "create", "-d", "test-external-volume-driver", "--name", "foo") 380 dockerCmd(c, "run", "-d", "--name", "testing", "-v", "foo:/bar", "busybox", "top") 381 382 var mounts []struct { 383 Name string 384 Driver string 385 } 386 out, err := inspectFieldJSON("testing", "Mounts") 387 c.Assert(err, checker.IsNil) 388 c.Assert(json.NewDecoder(strings.NewReader(out)).Decode(&mounts), checker.IsNil) 389 c.Assert(len(mounts), checker.Equals, 1, check.Commentf(out)) 390 c.Assert(mounts[0].Name, checker.Equals, "foo") 391 c.Assert(mounts[0].Driver, checker.Equals, "test-external-volume-driver") 392 } 393 394 func (s *DockerExternalVolumeSuite) TesttExternalVolumeDriverList(c *check.C) { 395 dockerCmd(c, "volume", "create", "-d", "test-external-volume-driver", "--name", "abc") 396 out, _ := dockerCmd(c, "volume", "ls") 397 ls := strings.Split(strings.TrimSpace(out), "\n") 398 c.Assert(len(ls), check.Equals, 2, check.Commentf("\n%s", out)) 399 400 vol := strings.Fields(ls[len(ls)-1]) 401 c.Assert(len(vol), check.Equals, 2, check.Commentf("%v", vol)) 402 c.Assert(vol[0], check.Equals, "test-external-volume-driver") 403 c.Assert(vol[1], check.Equals, "abc") 404 405 c.Assert(s.ec.lists, check.Equals, 1) 406 } 407 408 func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverGet(c *check.C) { 409 out, _, err := dockerCmdWithError("volume", "inspect", "dummy") 410 c.Assert(err, check.NotNil, check.Commentf(out)) 411 c.Assert(s.ec.gets, check.Equals, 1) 412 c.Assert(out, checker.Contains, "No such volume") 413 }