github.com/OpenFlowLabs/moby@v17.12.1-ce-rc2+incompatible/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", "application/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  		// TODO @gupta-ak: Figure out what to do here.
   202  		dir, err := driver.Get(req.ID, req.MountLabel)
   203  		if err != nil {
   204  			respond(w, err)
   205  			return
   206  		}
   207  		respond(w, &graphDriverResponse{Dir: dir.Path()})
   208  	})
   209  
   210  	mux.HandleFunc("/GraphDriver.Put", func(w http.ResponseWriter, r *http.Request) {
   211  		s.ec[ext].puts++
   212  
   213  		var req graphDriverRequest
   214  		if err := decReq(r.Body, &req, w); err != nil {
   215  			return
   216  		}
   217  
   218  		if err := driver.Put(req.ID); err != nil {
   219  			respond(w, err)
   220  			return
   221  		}
   222  		respond(w, "{}")
   223  	})
   224  
   225  	mux.HandleFunc("/GraphDriver.Exists", func(w http.ResponseWriter, r *http.Request) {
   226  		s.ec[ext].exists++
   227  
   228  		var req graphDriverRequest
   229  		if err := decReq(r.Body, &req, w); err != nil {
   230  			return
   231  		}
   232  		respond(w, &graphDriverResponse{Exists: driver.Exists(req.ID)})
   233  	})
   234  
   235  	mux.HandleFunc("/GraphDriver.Status", func(w http.ResponseWriter, r *http.Request) {
   236  		s.ec[ext].stats++
   237  		respond(w, &graphDriverResponse{Status: driver.Status()})
   238  	})
   239  
   240  	mux.HandleFunc("/GraphDriver.Cleanup", func(w http.ResponseWriter, r *http.Request) {
   241  		s.ec[ext].cleanups++
   242  		err := driver.Cleanup()
   243  		if err != nil {
   244  			respond(w, err)
   245  			return
   246  		}
   247  		respond(w, `{}`)
   248  	})
   249  
   250  	mux.HandleFunc("/GraphDriver.GetMetadata", func(w http.ResponseWriter, r *http.Request) {
   251  		s.ec[ext].metadata++
   252  
   253  		var req graphDriverRequest
   254  		if err := decReq(r.Body, &req, w); err != nil {
   255  			return
   256  		}
   257  
   258  		data, err := driver.GetMetadata(req.ID)
   259  		if err != nil {
   260  			respond(w, err)
   261  			return
   262  		}
   263  		respond(w, &graphDriverResponse{Metadata: data})
   264  	})
   265  
   266  	mux.HandleFunc("/GraphDriver.Diff", func(w http.ResponseWriter, r *http.Request) {
   267  		s.ec[ext].diff++
   268  
   269  		var req graphDriverRequest
   270  		if err := decReq(r.Body, &req, w); err != nil {
   271  			return
   272  		}
   273  
   274  		diff, err := driver.Diff(req.ID, req.Parent)
   275  		if err != nil {
   276  			respond(w, err)
   277  			return
   278  		}
   279  		io.Copy(w, diff)
   280  	})
   281  
   282  	mux.HandleFunc("/GraphDriver.Changes", func(w http.ResponseWriter, r *http.Request) {
   283  		s.ec[ext].changes++
   284  		var req graphDriverRequest
   285  		if err := decReq(r.Body, &req, w); err != nil {
   286  			return
   287  		}
   288  
   289  		changes, err := driver.Changes(req.ID, req.Parent)
   290  		if err != nil {
   291  			respond(w, err)
   292  			return
   293  		}
   294  		respond(w, &graphDriverResponse{Changes: changes})
   295  	})
   296  
   297  	mux.HandleFunc("/GraphDriver.ApplyDiff", func(w http.ResponseWriter, r *http.Request) {
   298  		s.ec[ext].applydiff++
   299  		diff := r.Body
   300  		defer r.Body.Close()
   301  
   302  		id := r.URL.Query().Get("id")
   303  		parent := r.URL.Query().Get("parent")
   304  
   305  		if id == "" {
   306  			http.Error(w, fmt.Sprintf("missing id"), 409)
   307  		}
   308  
   309  		size, err := driver.ApplyDiff(id, parent, diff)
   310  		if err != nil {
   311  			respond(w, err)
   312  			return
   313  		}
   314  		respond(w, &graphDriverResponse{Size: size})
   315  	})
   316  
   317  	mux.HandleFunc("/GraphDriver.DiffSize", func(w http.ResponseWriter, r *http.Request) {
   318  		s.ec[ext].diffsize++
   319  
   320  		var req graphDriverRequest
   321  		if err := decReq(r.Body, &req, w); err != nil {
   322  			return
   323  		}
   324  
   325  		size, err := driver.DiffSize(req.ID, req.Parent)
   326  		if err != nil {
   327  			respond(w, err)
   328  			return
   329  		}
   330  		respond(w, &graphDriverResponse{Size: size})
   331  	})
   332  
   333  	err = os.MkdirAll("/etc/docker/plugins", 0755)
   334  	c.Assert(err, check.IsNil, check.Commentf("error creating /etc/docker/plugins"))
   335  
   336  	specFile := "/etc/docker/plugins/" + name + "." + ext
   337  	err = ioutil.WriteFile(specFile, b, 0644)
   338  	c.Assert(err, check.IsNil, check.Commentf("error writing to %s", specFile))
   339  }
   340  
   341  func (s *DockerExternalGraphdriverSuite) TearDownSuite(c *check.C) {
   342  	s.server.Close()
   343  	s.jserver.Close()
   344  
   345  	err := os.RemoveAll("/etc/docker/plugins")
   346  	c.Assert(err, check.IsNil, check.Commentf("error removing /etc/docker/plugins"))
   347  }
   348  
   349  func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriver(c *check.C) {
   350  	testRequires(c, ExperimentalDaemon)
   351  
   352  	s.testExternalGraphDriver("test-external-graph-driver", "spec", c)
   353  	s.testExternalGraphDriver("json-external-graph-driver", "json", c)
   354  }
   355  
   356  func (s *DockerExternalGraphdriverSuite) testExternalGraphDriver(name string, ext string, c *check.C) {
   357  	s.d.StartWithBusybox(c, "-s", name)
   358  
   359  	out, err := s.d.Cmd("run", "--name=graphtest", "busybox", "sh", "-c", "echo hello > /hello")
   360  	c.Assert(err, check.IsNil, check.Commentf(out))
   361  
   362  	s.d.Restart(c, "-s", name)
   363  
   364  	out, err = s.d.Cmd("inspect", "--format={{.GraphDriver.Name}}", "graphtest")
   365  	c.Assert(err, check.IsNil, check.Commentf(out))
   366  	c.Assert(strings.TrimSpace(out), check.Equals, name)
   367  
   368  	out, err = s.d.Cmd("diff", "graphtest")
   369  	c.Assert(err, check.IsNil, check.Commentf(out))
   370  	c.Assert(strings.Contains(out, "A /hello"), check.Equals, true, check.Commentf("diff output: %s", out))
   371  
   372  	out, err = s.d.Cmd("rm", "-f", "graphtest")
   373  	c.Assert(err, check.IsNil, check.Commentf(out))
   374  
   375  	out, err = s.d.Cmd("info")
   376  	c.Assert(err, check.IsNil, check.Commentf(out))
   377  
   378  	s.d.Stop(c)
   379  
   380  	// Don't check s.ec.exists, because the daemon no longer calls the
   381  	// Exists function.
   382  	c.Assert(s.ec[ext].activations, check.Equals, 2)
   383  	c.Assert(s.ec[ext].init, check.Equals, 2)
   384  	c.Assert(s.ec[ext].creations >= 1, check.Equals, true)
   385  	c.Assert(s.ec[ext].removals >= 1, check.Equals, true)
   386  	c.Assert(s.ec[ext].gets >= 1, check.Equals, true)
   387  	c.Assert(s.ec[ext].puts >= 1, check.Equals, true)
   388  	c.Assert(s.ec[ext].stats, check.Equals, 5)
   389  	c.Assert(s.ec[ext].cleanups, check.Equals, 2)
   390  	c.Assert(s.ec[ext].applydiff >= 1, check.Equals, true)
   391  	c.Assert(s.ec[ext].changes, check.Equals, 1)
   392  	c.Assert(s.ec[ext].diffsize, check.Equals, 0)
   393  	c.Assert(s.ec[ext].diff, check.Equals, 0)
   394  	c.Assert(s.ec[ext].metadata, check.Equals, 1)
   395  }
   396  
   397  func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriverPull(c *check.C) {
   398  	testRequires(c, Network, ExperimentalDaemon)
   399  
   400  	s.d.Start(c)
   401  
   402  	out, err := s.d.Cmd("pull", "busybox:latest")
   403  	c.Assert(err, check.IsNil, check.Commentf(out))
   404  
   405  	out, err = s.d.Cmd("run", "-d", "busybox", "top")
   406  	c.Assert(err, check.IsNil, check.Commentf(out))
   407  }