docker_cli_external_graphdriver_unix_test.go 11 KB

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