docker_cli_external_graphdriver_unix_test.go 11 KB

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