docker_cli_external_graphdriver_unix_test.go 11 KB

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