docker_cli_external_graphdriver_unix_test.go 11 KB

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