docker_cli_external_graphdriver_unix_test.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. // +build experimental
  2. // +build !windows
  3. package main
  4. import (
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "net/http"
  10. "net/http/httptest"
  11. "os"
  12. "strings"
  13. "github.com/docker/docker/daemon/graphdriver"
  14. "github.com/docker/docker/daemon/graphdriver/vfs"
  15. "github.com/docker/docker/pkg/archive"
  16. "github.com/go-check/check"
  17. )
  18. func init() {
  19. check.Suite(&DockerExternalGraphdriverSuite{
  20. ds: &DockerSuite{},
  21. })
  22. }
  23. type DockerExternalGraphdriverSuite struct {
  24. server *httptest.Server
  25. ds *DockerSuite
  26. d *Daemon
  27. ec *graphEventsCounter
  28. }
  29. type graphEventsCounter struct {
  30. activations int
  31. creations int
  32. removals int
  33. gets int
  34. puts int
  35. stats int
  36. cleanups int
  37. exists int
  38. init int
  39. metadata int
  40. diff int
  41. applydiff int
  42. changes int
  43. diffsize int
  44. }
  45. func (s *DockerExternalGraphdriverSuite) SetUpTest(c *check.C) {
  46. s.d = NewDaemon(c)
  47. s.ec = &graphEventsCounter{}
  48. }
  49. func (s *DockerExternalGraphdriverSuite) TearDownTest(c *check.C) {
  50. s.d.Stop()
  51. s.ds.TearDownTest(c)
  52. }
  53. func (s *DockerExternalGraphdriverSuite) SetUpSuite(c *check.C) {
  54. mux := http.NewServeMux()
  55. s.server = httptest.NewServer(mux)
  56. type graphDriverRequest struct {
  57. ID string `json:",omitempty"`
  58. Parent string `json:",omitempty"`
  59. MountLabel string `json:",omitempty"`
  60. ReadOnly bool `json:",omitempty"`
  61. }
  62. type graphDriverResponse struct {
  63. Err error `json:",omitempty"`
  64. Dir string `json:",omitempty"`
  65. Exists bool `json:",omitempty"`
  66. Status [][2]string `json:",omitempty"`
  67. Metadata map[string]string `json:",omitempty"`
  68. Changes []archive.Change `json:",omitempty"`
  69. Size int64 `json:",omitempty"`
  70. }
  71. respond := func(w http.ResponseWriter, data interface{}) {
  72. w.Header().Set("Content-Type", "appplication/vnd.docker.plugins.v1+json")
  73. switch t := data.(type) {
  74. case error:
  75. fmt.Fprintln(w, fmt.Sprintf(`{"Err": %q}`, t.Error()))
  76. case string:
  77. fmt.Fprintln(w, t)
  78. default:
  79. json.NewEncoder(w).Encode(&data)
  80. }
  81. }
  82. decReq := func(b io.ReadCloser, out interface{}, w http.ResponseWriter) error {
  83. defer b.Close()
  84. if err := json.NewDecoder(b).Decode(&out); err != nil {
  85. http.Error(w, fmt.Sprintf("error decoding json: %s", err.Error()), 500)
  86. }
  87. return nil
  88. }
  89. base, err := ioutil.TempDir("", "external-graph-test")
  90. c.Assert(err, check.IsNil)
  91. vfsProto, err := vfs.Init(base, []string{}, nil, nil)
  92. c.Assert(err, check.IsNil, check.Commentf("error initializing graph driver"))
  93. driver := graphdriver.NewNaiveDiffDriver(vfsProto, nil, nil)
  94. mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
  95. s.ec.activations++
  96. respond(w, `{"Implements": ["GraphDriver"]}`)
  97. })
  98. mux.HandleFunc("/GraphDriver.Init", func(w http.ResponseWriter, r *http.Request) {
  99. s.ec.init++
  100. respond(w, "{}")
  101. })
  102. mux.HandleFunc("/GraphDriver.CreateReadWrite", func(w http.ResponseWriter, r *http.Request) {
  103. s.ec.creations++
  104. var req graphDriverRequest
  105. if err := decReq(r.Body, &req, w); err != nil {
  106. return
  107. }
  108. if err := driver.CreateReadWrite(req.ID, req.Parent, "", nil); err != nil {
  109. respond(w, err)
  110. return
  111. }
  112. respond(w, "{}")
  113. })
  114. mux.HandleFunc("/GraphDriver.Create", func(w http.ResponseWriter, r *http.Request) {
  115. s.ec.creations++
  116. var req graphDriverRequest
  117. if err := decReq(r.Body, &req, w); err != nil {
  118. return
  119. }
  120. if err := driver.Create(req.ID, req.Parent, "", nil); err != nil {
  121. respond(w, err)
  122. return
  123. }
  124. respond(w, "{}")
  125. })
  126. mux.HandleFunc("/GraphDriver.Remove", func(w http.ResponseWriter, r *http.Request) {
  127. s.ec.removals++
  128. var req graphDriverRequest
  129. if err := decReq(r.Body, &req, w); err != nil {
  130. return
  131. }
  132. if err := driver.Remove(req.ID); err != nil {
  133. respond(w, err)
  134. return
  135. }
  136. respond(w, "{}")
  137. })
  138. mux.HandleFunc("/GraphDriver.Get", func(w http.ResponseWriter, r *http.Request) {
  139. s.ec.gets++
  140. var req graphDriverRequest
  141. if err := decReq(r.Body, &req, w); err != nil {
  142. return
  143. }
  144. dir, err := driver.Get(req.ID, req.MountLabel)
  145. if err != nil {
  146. respond(w, err)
  147. return
  148. }
  149. respond(w, &graphDriverResponse{Dir: dir})
  150. })
  151. mux.HandleFunc("/GraphDriver.Put", func(w http.ResponseWriter, r *http.Request) {
  152. s.ec.puts++
  153. var req graphDriverRequest
  154. if err := decReq(r.Body, &req, w); err != nil {
  155. return
  156. }
  157. if err := driver.Put(req.ID); err != nil {
  158. respond(w, err)
  159. return
  160. }
  161. respond(w, "{}")
  162. })
  163. mux.HandleFunc("/GraphDriver.Exists", func(w http.ResponseWriter, r *http.Request) {
  164. s.ec.exists++
  165. var req graphDriverRequest
  166. if err := decReq(r.Body, &req, w); err != nil {
  167. return
  168. }
  169. respond(w, &graphDriverResponse{Exists: driver.Exists(req.ID)})
  170. })
  171. mux.HandleFunc("/GraphDriver.Status", func(w http.ResponseWriter, r *http.Request) {
  172. s.ec.stats++
  173. respond(w, &graphDriverResponse{Status: driver.Status()})
  174. })
  175. mux.HandleFunc("/GraphDriver.Cleanup", func(w http.ResponseWriter, r *http.Request) {
  176. s.ec.cleanups++
  177. err := driver.Cleanup()
  178. if err != nil {
  179. respond(w, err)
  180. return
  181. }
  182. respond(w, `{}`)
  183. })
  184. mux.HandleFunc("/GraphDriver.GetMetadata", func(w http.ResponseWriter, r *http.Request) {
  185. s.ec.metadata++
  186. var req graphDriverRequest
  187. if err := decReq(r.Body, &req, w); err != nil {
  188. return
  189. }
  190. data, err := driver.GetMetadata(req.ID)
  191. if err != nil {
  192. respond(w, err)
  193. return
  194. }
  195. respond(w, &graphDriverResponse{Metadata: data})
  196. })
  197. mux.HandleFunc("/GraphDriver.Diff", func(w http.ResponseWriter, r *http.Request) {
  198. s.ec.diff++
  199. var req graphDriverRequest
  200. if err := decReq(r.Body, &req, w); err != nil {
  201. return
  202. }
  203. diff, err := driver.Diff(req.ID, req.Parent)
  204. if err != nil {
  205. respond(w, err)
  206. return
  207. }
  208. io.Copy(w, diff)
  209. })
  210. mux.HandleFunc("/GraphDriver.Changes", func(w http.ResponseWriter, r *http.Request) {
  211. s.ec.changes++
  212. var req graphDriverRequest
  213. if err := decReq(r.Body, &req, w); err != nil {
  214. return
  215. }
  216. changes, err := driver.Changes(req.ID, req.Parent)
  217. if err != nil {
  218. respond(w, err)
  219. return
  220. }
  221. respond(w, &graphDriverResponse{Changes: changes})
  222. })
  223. mux.HandleFunc("/GraphDriver.ApplyDiff", func(w http.ResponseWriter, r *http.Request) {
  224. s.ec.applydiff++
  225. var diff archive.Reader = r.Body
  226. defer r.Body.Close()
  227. id := r.URL.Query().Get("id")
  228. parent := r.URL.Query().Get("parent")
  229. if id == "" {
  230. http.Error(w, fmt.Sprintf("missing id"), 409)
  231. }
  232. size, err := driver.ApplyDiff(id, parent, diff)
  233. if err != nil {
  234. respond(w, err)
  235. return
  236. }
  237. respond(w, &graphDriverResponse{Size: size})
  238. })
  239. mux.HandleFunc("/GraphDriver.DiffSize", func(w http.ResponseWriter, r *http.Request) {
  240. s.ec.diffsize++
  241. var req graphDriverRequest
  242. if err := decReq(r.Body, &req, w); err != nil {
  243. return
  244. }
  245. size, err := driver.DiffSize(req.ID, req.Parent)
  246. if err != nil {
  247. respond(w, err)
  248. return
  249. }
  250. respond(w, &graphDriverResponse{Size: size})
  251. })
  252. err = os.MkdirAll("/etc/docker/plugins", 0755)
  253. c.Assert(err, check.IsNil, check.Commentf("error creating /etc/docker/plugins"))
  254. err = ioutil.WriteFile("/etc/docker/plugins/test-external-graph-driver.spec", []byte(s.server.URL), 0644)
  255. c.Assert(err, check.IsNil, check.Commentf("error writing to /etc/docker/plugins/test-external-graph-driver.spec"))
  256. }
  257. func (s *DockerExternalGraphdriverSuite) TearDownSuite(c *check.C) {
  258. s.server.Close()
  259. err := os.RemoveAll("/etc/docker/plugins")
  260. c.Assert(err, check.IsNil, check.Commentf("error removing /etc/docker/plugins"))
  261. }
  262. func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriver(c *check.C) {
  263. if err := s.d.StartWithBusybox("-s", "test-external-graph-driver"); err != nil {
  264. b, _ := ioutil.ReadFile(s.d.LogFileName())
  265. c.Assert(err, check.IsNil, check.Commentf("\n%s", string(b)))
  266. }
  267. out, err := s.d.Cmd("run", "-d", "--name=graphtest", "busybox", "sh", "-c", "echo hello > /hello")
  268. c.Assert(err, check.IsNil, check.Commentf(out))
  269. err = s.d.Restart("-s", "test-external-graph-driver")
  270. out, err = s.d.Cmd("inspect", "--format='{{.GraphDriver.Name}}'", "graphtest")
  271. c.Assert(err, check.IsNil, check.Commentf(out))
  272. c.Assert(strings.TrimSpace(out), check.Equals, "test-external-graph-driver")
  273. out, err = s.d.Cmd("diff", "graphtest")
  274. c.Assert(err, check.IsNil, check.Commentf(out))
  275. c.Assert(strings.Contains(out, "A /hello"), check.Equals, true)
  276. out, err = s.d.Cmd("rm", "-f", "graphtest")
  277. c.Assert(err, check.IsNil, check.Commentf(out))
  278. out, err = s.d.Cmd("info")
  279. c.Assert(err, check.IsNil, check.Commentf(out))
  280. err = s.d.Stop()
  281. c.Assert(err, check.IsNil)
  282. // Don't check s.ec.exists, because the daemon no longer calls the
  283. // Exists function.
  284. c.Assert(s.ec.activations, check.Equals, 2)
  285. c.Assert(s.ec.init, check.Equals, 2)
  286. c.Assert(s.ec.creations >= 1, check.Equals, true)
  287. c.Assert(s.ec.removals >= 1, check.Equals, true)
  288. c.Assert(s.ec.gets >= 1, check.Equals, true)
  289. c.Assert(s.ec.puts >= 1, check.Equals, true)
  290. c.Assert(s.ec.stats, check.Equals, 3)
  291. c.Assert(s.ec.cleanups, check.Equals, 2)
  292. c.Assert(s.ec.applydiff >= 1, check.Equals, true)
  293. c.Assert(s.ec.changes, check.Equals, 1)
  294. c.Assert(s.ec.diffsize, check.Equals, 0)
  295. c.Assert(s.ec.diff, check.Equals, 0)
  296. c.Assert(s.ec.metadata, check.Equals, 1)
  297. }
  298. func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriverPull(c *check.C) {
  299. testRequires(c, Network)
  300. c.Assert(s.d.Start(), check.IsNil)
  301. out, err := s.d.Cmd("pull", "busybox:latest")
  302. c.Assert(err, check.IsNil, check.Commentf(out))
  303. out, err = s.d.Cmd("run", "-d", "busybox", "top")
  304. c.Assert(err, check.IsNil, check.Commentf(out))
  305. }