docker_cli_external_graphdriver_unix_test.go 8.8 KB

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