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