docker_cli_external_graphdriver_unix_test.go 11 KB

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