docker_cli_external_graphdriver_unix_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  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.Plugin{Name: "json-external-graph-driver", Addr: 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. }
  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.Create", 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.Create(req.ID, req.Parent, ""); err != nil {
  126. respond(w, err)
  127. return
  128. }
  129. respond(w, "{}")
  130. })
  131. mux.HandleFunc("/GraphDriver.Remove", func(w http.ResponseWriter, r *http.Request) {
  132. s.ec[ext].removals++
  133. var req graphDriverRequest
  134. if err := decReq(r.Body, &req, w); err != nil {
  135. return
  136. }
  137. if err := driver.Remove(req.ID); err != nil {
  138. respond(w, err)
  139. return
  140. }
  141. respond(w, "{}")
  142. })
  143. mux.HandleFunc("/GraphDriver.Get", func(w http.ResponseWriter, r *http.Request) {
  144. s.ec[ext].gets++
  145. var req graphDriverRequest
  146. if err := decReq(r.Body, &req, w); err != nil {
  147. return
  148. }
  149. dir, err := driver.Get(req.ID, req.MountLabel)
  150. if err != nil {
  151. respond(w, err)
  152. return
  153. }
  154. respond(w, &graphDriverResponse{Dir: dir})
  155. })
  156. mux.HandleFunc("/GraphDriver.Put", func(w http.ResponseWriter, r *http.Request) {
  157. s.ec[ext].puts++
  158. var req graphDriverRequest
  159. if err := decReq(r.Body, &req, w); err != nil {
  160. return
  161. }
  162. if err := driver.Put(req.ID); err != nil {
  163. respond(w, err)
  164. return
  165. }
  166. respond(w, "{}")
  167. })
  168. mux.HandleFunc("/GraphDriver.Exists", func(w http.ResponseWriter, r *http.Request) {
  169. s.ec[ext].exists++
  170. var req graphDriverRequest
  171. if err := decReq(r.Body, &req, w); err != nil {
  172. return
  173. }
  174. respond(w, &graphDriverResponse{Exists: driver.Exists(req.ID)})
  175. })
  176. mux.HandleFunc("/GraphDriver.Status", func(w http.ResponseWriter, r *http.Request) {
  177. s.ec[ext].stats++
  178. respond(w, &graphDriverResponse{Status: driver.Status()})
  179. })
  180. mux.HandleFunc("/GraphDriver.Cleanup", func(w http.ResponseWriter, r *http.Request) {
  181. s.ec[ext].cleanups++
  182. err := driver.Cleanup()
  183. if err != nil {
  184. respond(w, err)
  185. return
  186. }
  187. respond(w, `{}`)
  188. })
  189. mux.HandleFunc("/GraphDriver.GetMetadata", func(w http.ResponseWriter, r *http.Request) {
  190. s.ec[ext].metadata++
  191. var req graphDriverRequest
  192. if err := decReq(r.Body, &req, w); err != nil {
  193. return
  194. }
  195. data, err := driver.GetMetadata(req.ID)
  196. if err != nil {
  197. respond(w, err)
  198. return
  199. }
  200. respond(w, &graphDriverResponse{Metadata: data})
  201. })
  202. mux.HandleFunc("/GraphDriver.Diff", func(w http.ResponseWriter, r *http.Request) {
  203. s.ec[ext].diff++
  204. var req graphDriverRequest
  205. if err := decReq(r.Body, &req, w); err != nil {
  206. return
  207. }
  208. diff, err := driver.Diff(req.ID, req.Parent)
  209. if err != nil {
  210. respond(w, err)
  211. return
  212. }
  213. io.Copy(w, diff)
  214. })
  215. mux.HandleFunc("/GraphDriver.Changes", func(w http.ResponseWriter, r *http.Request) {
  216. s.ec[ext].changes++
  217. var req graphDriverRequest
  218. if err := decReq(r.Body, &req, w); err != nil {
  219. return
  220. }
  221. changes, err := driver.Changes(req.ID, req.Parent)
  222. if err != nil {
  223. respond(w, err)
  224. return
  225. }
  226. respond(w, &graphDriverResponse{Changes: changes})
  227. })
  228. mux.HandleFunc("/GraphDriver.ApplyDiff", func(w http.ResponseWriter, r *http.Request) {
  229. s.ec[ext].applydiff++
  230. var diff archive.Reader = r.Body
  231. defer r.Body.Close()
  232. id := r.URL.Query().Get("id")
  233. parent := r.URL.Query().Get("parent")
  234. if id == "" {
  235. http.Error(w, fmt.Sprintf("missing id"), 409)
  236. }
  237. size, err := driver.ApplyDiff(id, parent, diff)
  238. if err != nil {
  239. respond(w, err)
  240. return
  241. }
  242. respond(w, &graphDriverResponse{Size: size})
  243. })
  244. mux.HandleFunc("/GraphDriver.DiffSize", func(w http.ResponseWriter, r *http.Request) {
  245. s.ec[ext].diffsize++
  246. var req graphDriverRequest
  247. if err := decReq(r.Body, &req, w); err != nil {
  248. return
  249. }
  250. size, err := driver.DiffSize(req.ID, req.Parent)
  251. if err != nil {
  252. respond(w, err)
  253. return
  254. }
  255. respond(w, &graphDriverResponse{Size: size})
  256. })
  257. err = os.MkdirAll("/etc/docker/plugins", 0755)
  258. c.Assert(err, check.IsNil, check.Commentf("error creating /etc/docker/plugins"))
  259. specFile := "/etc/docker/plugins/" + name + "." + ext
  260. err = ioutil.WriteFile(specFile, b, 0644)
  261. c.Assert(err, check.IsNil, check.Commentf("error writing to %s", specFile))
  262. }
  263. func (s *DockerExternalGraphdriverSuite) TearDownSuite(c *check.C) {
  264. s.server.Close()
  265. s.jserver.Close()
  266. err := os.RemoveAll("/etc/docker/plugins")
  267. c.Assert(err, check.IsNil, check.Commentf("error removing /etc/docker/plugins"))
  268. }
  269. func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriver(c *check.C) {
  270. s.testExternalGraphDriver("test-external-graph-driver", "spec", c)
  271. s.testExternalGraphDriver("json-external-graph-driver", "json", c)
  272. }
  273. func (s *DockerExternalGraphdriverSuite) testExternalGraphDriver(name string, ext string, c *check.C) {
  274. if err := s.d.StartWithBusybox("-s", name); err != nil {
  275. b, _ := ioutil.ReadFile(s.d.LogFileName())
  276. c.Assert(err, check.IsNil, check.Commentf("\n%s", string(b)))
  277. }
  278. out, err := s.d.Cmd("run", "-d", "--name=graphtest", "busybox", "sh", "-c", "echo hello > /hello")
  279. c.Assert(err, check.IsNil, check.Commentf(out))
  280. err = s.d.Restart("-s", name)
  281. out, err = s.d.Cmd("inspect", "--format='{{.GraphDriver.Name}}'", "graphtest")
  282. c.Assert(err, check.IsNil, check.Commentf(out))
  283. c.Assert(strings.TrimSpace(out), check.Equals, name)
  284. out, err = s.d.Cmd("diff", "graphtest")
  285. c.Assert(err, check.IsNil, check.Commentf(out))
  286. c.Assert(strings.Contains(out, "A /hello"), check.Equals, true)
  287. out, err = s.d.Cmd("rm", "-f", "graphtest")
  288. c.Assert(err, check.IsNil, check.Commentf(out))
  289. out, err = s.d.Cmd("info")
  290. c.Assert(err, check.IsNil, check.Commentf(out))
  291. err = s.d.Stop()
  292. c.Assert(err, check.IsNil)
  293. // Don't check s.ec.exists, because the daemon no longer calls the
  294. // Exists function.
  295. c.Assert(s.ec[ext].activations, check.Equals, 2)
  296. c.Assert(s.ec[ext].init, check.Equals, 2)
  297. c.Assert(s.ec[ext].creations >= 1, check.Equals, true)
  298. c.Assert(s.ec[ext].removals >= 1, check.Equals, true)
  299. c.Assert(s.ec[ext].gets >= 1, check.Equals, true)
  300. c.Assert(s.ec[ext].puts >= 1, check.Equals, true)
  301. c.Assert(s.ec[ext].stats, check.Equals, 3)
  302. c.Assert(s.ec[ext].cleanups, check.Equals, 2)
  303. c.Assert(s.ec[ext].applydiff >= 1, check.Equals, true)
  304. c.Assert(s.ec[ext].changes, check.Equals, 1)
  305. c.Assert(s.ec[ext].diffsize, check.Equals, 0)
  306. c.Assert(s.ec[ext].diff, check.Equals, 0)
  307. c.Assert(s.ec[ext].metadata, check.Equals, 1)
  308. }
  309. func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriverPull(c *check.C) {
  310. testRequires(c, Network)
  311. c.Assert(s.d.Start(), check.IsNil)
  312. out, err := s.d.Cmd("pull", "busybox:latest")
  313. c.Assert(err, check.IsNil, check.Commentf(out))
  314. out, err = s.d.Cmd("run", "-d", "busybox", "top")
  315. c.Assert(err, check.IsNil, check.Commentf(out))
  316. }