docker_cli_external_graphdriver_unix_test.go 11 KB

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