docker_cli_external_graphdriver_unix_test.go 11 KB

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