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