github.com/reds/docker@v1.11.2-rc1/integration-cli/docker_cli_external_graphdriver_unix_test.go (about)

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