github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/integration-cli/docker_cli_external_graphdriver_unix_test.go (about)

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