blob: 9fd36ec9c114fd5817865e27e9a2cce48f56eb6f [file] [log] [blame]
// +build experimental
// +build !windows
package main
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"strings"
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/daemon/graphdriver/vfs"
"github.com/docker/docker/pkg/archive"
"github.com/go-check/check"
)
func init() {
check.Suite(&DockerExternalGraphdriverSuite{
ds: &DockerSuite{},
})
}
type DockerExternalGraphdriverSuite struct {
server *httptest.Server
ds *DockerSuite
d *Daemon
ec *graphEventsCounter
}
type graphEventsCounter struct {
activations int
creations int
removals int
gets int
puts int
stats int
cleanups int
exists int
init int
metadata int
diff int
applydiff int
changes int
diffsize int
}
func (s *DockerExternalGraphdriverSuite) SetUpTest(c *check.C) {
s.d = NewDaemon(c)
s.ec = &graphEventsCounter{}
}
func (s *DockerExternalGraphdriverSuite) TearDownTest(c *check.C) {
s.d.Stop()
s.ds.TearDownTest(c)
}
func (s *DockerExternalGraphdriverSuite) SetUpSuite(c *check.C) {
mux := http.NewServeMux()
s.server = httptest.NewServer(mux)
type graphDriverRequest struct {
ID string `json:",omitempty"`
Parent string `json:",omitempty"`
MountLabel string `json:",omitempty"`
}
type graphDriverResponse struct {
Err error `json:",omitempty"`
Dir string `json:",omitempty"`
Exists bool `json:",omitempty"`
Status [][2]string `json:",omitempty"`
Metadata map[string]string `json:",omitempty"`
Changes []archive.Change `json:",omitempty"`
Size int64 `json:",omitempty"`
}
respond := func(w http.ResponseWriter, data interface{}) {
w.Header().Set("Content-Type", "appplication/vnd.docker.plugins.v1+json")
switch t := data.(type) {
case error:
fmt.Fprintln(w, fmt.Sprintf(`{"Err": %q}`, t.Error()))
case string:
fmt.Fprintln(w, t)
default:
json.NewEncoder(w).Encode(&data)
}
}
decReq := func(b io.ReadCloser, out interface{}, w http.ResponseWriter) error {
defer b.Close()
if err := json.NewDecoder(b).Decode(&out); err != nil {
http.Error(w, fmt.Sprintf("error decoding json: %s", err.Error()), 500)
}
return nil
}
base, err := ioutil.TempDir("", "external-graph-test")
c.Assert(err, check.IsNil)
vfsProto, err := vfs.Init(base, []string{}, nil, nil)
c.Assert(err, check.IsNil, check.Commentf("error initializing graph driver"))
driver := graphdriver.NewNaiveDiffDriver(vfsProto, nil, nil)
mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
s.ec.activations++
respond(w, `{"Implements": ["GraphDriver"]}`)
})
mux.HandleFunc("/GraphDriver.Init", func(w http.ResponseWriter, r *http.Request) {
s.ec.init++
respond(w, "{}")
})
mux.HandleFunc("/GraphDriver.Create", func(w http.ResponseWriter, r *http.Request) {
s.ec.creations++
var req graphDriverRequest
if err := decReq(r.Body, &req, w); err != nil {
return
}
if err := driver.Create(req.ID, req.Parent, ""); err != nil {
respond(w, err)
return
}
respond(w, "{}")
})
mux.HandleFunc("/GraphDriver.Remove", func(w http.ResponseWriter, r *http.Request) {
s.ec.removals++
var req graphDriverRequest
if err := decReq(r.Body, &req, w); err != nil {
return
}
if err := driver.Remove(req.ID); err != nil {
respond(w, err)
return
}
respond(w, "{}")
})
mux.HandleFunc("/GraphDriver.Get", func(w http.ResponseWriter, r *http.Request) {
s.ec.gets++
var req graphDriverRequest
if err := decReq(r.Body, &req, w); err != nil {
return
}
dir, err := driver.Get(req.ID, req.MountLabel)
if err != nil {
respond(w, err)
return
}
respond(w, &graphDriverResponse{Dir: dir})
})
mux.HandleFunc("/GraphDriver.Put", func(w http.ResponseWriter, r *http.Request) {
s.ec.puts++
var req graphDriverRequest
if err := decReq(r.Body, &req, w); err != nil {
return
}
if err := driver.Put(req.ID); err != nil {
respond(w, err)
return
}
respond(w, "{}")
})
mux.HandleFunc("/GraphDriver.Exists", func(w http.ResponseWriter, r *http.Request) {
s.ec.exists++
var req graphDriverRequest
if err := decReq(r.Body, &req, w); err != nil {
return
}
respond(w, &graphDriverResponse{Exists: driver.Exists(req.ID)})
})
mux.HandleFunc("/GraphDriver.Status", func(w http.ResponseWriter, r *http.Request) {
s.ec.stats++
respond(w, &graphDriverResponse{Status: driver.Status()})
})
mux.HandleFunc("/GraphDriver.Cleanup", func(w http.ResponseWriter, r *http.Request) {
s.ec.cleanups++
err := driver.Cleanup()
if err != nil {
respond(w, err)
return
}
respond(w, `{}`)
})
mux.HandleFunc("/GraphDriver.GetMetadata", func(w http.ResponseWriter, r *http.Request) {
s.ec.metadata++
var req graphDriverRequest
if err := decReq(r.Body, &req, w); err != nil {
return
}
data, err := driver.GetMetadata(req.ID)
if err != nil {
respond(w, err)
return
}
respond(w, &graphDriverResponse{Metadata: data})
})
mux.HandleFunc("/GraphDriver.Diff", func(w http.ResponseWriter, r *http.Request) {
s.ec.diff++
var req graphDriverRequest
if err := decReq(r.Body, &req, w); err != nil {
return
}
diff, err := driver.Diff(req.ID, req.Parent)
if err != nil {
respond(w, err)
return
}
io.Copy(w, diff)
})
mux.HandleFunc("/GraphDriver.Changes", func(w http.ResponseWriter, r *http.Request) {
s.ec.changes++
var req graphDriverRequest
if err := decReq(r.Body, &req, w); err != nil {
return
}
changes, err := driver.Changes(req.ID, req.Parent)
if err != nil {
respond(w, err)
return
}
respond(w, &graphDriverResponse{Changes: changes})
})
mux.HandleFunc("/GraphDriver.ApplyDiff", func(w http.ResponseWriter, r *http.Request) {
s.ec.applydiff++
var diff archive.Reader = r.Body
defer r.Body.Close()
id := r.URL.Query().Get("id")
parent := r.URL.Query().Get("parent")
if id == "" {
http.Error(w, fmt.Sprintf("missing id"), 409)
}
size, err := driver.ApplyDiff(id, parent, diff)
if err != nil {
respond(w, err)
return
}
respond(w, &graphDriverResponse{Size: size})
})
mux.HandleFunc("/GraphDriver.DiffSize", func(w http.ResponseWriter, r *http.Request) {
s.ec.diffsize++
var req graphDriverRequest
if err := decReq(r.Body, &req, w); err != nil {
return
}
size, err := driver.DiffSize(req.ID, req.Parent)
if err != nil {
respond(w, err)
return
}
respond(w, &graphDriverResponse{Size: size})
})
err = os.MkdirAll("/etc/docker/plugins", 0755)
c.Assert(err, check.IsNil, check.Commentf("error creating /etc/docker/plugins"))
err = ioutil.WriteFile("/etc/docker/plugins/test-external-graph-driver.spec", []byte(s.server.URL), 0644)
c.Assert(err, check.IsNil, check.Commentf("error writing to /etc/docker/plugins/test-external-graph-driver.spec"))
}
func (s *DockerExternalGraphdriverSuite) TearDownSuite(c *check.C) {
s.server.Close()
err := os.RemoveAll("/etc/docker/plugins")
c.Assert(err, check.IsNil, check.Commentf("error removing /etc/docker/plugins"))
}
func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriver(c *check.C) {
if err := s.d.StartWithBusybox("-s", "test-external-graph-driver"); err != nil {
b, _ := ioutil.ReadFile(s.d.LogFileName())
c.Assert(err, check.IsNil, check.Commentf("\n%s", string(b)))
}
out, err := s.d.Cmd("run", "-d", "--name=graphtest", "busybox", "sh", "-c", "echo hello > /hello")
c.Assert(err, check.IsNil, check.Commentf(out))
err = s.d.Restart("-s", "test-external-graph-driver")
out, err = s.d.Cmd("inspect", "--format='{{.GraphDriver.Name}}'", "graphtest")
c.Assert(err, check.IsNil, check.Commentf(out))
c.Assert(strings.TrimSpace(out), check.Equals, "test-external-graph-driver")
out, err = s.d.Cmd("diff", "graphtest")
c.Assert(err, check.IsNil, check.Commentf(out))
c.Assert(strings.Contains(out, "A /hello"), check.Equals, true)
out, err = s.d.Cmd("rm", "-f", "graphtest")
c.Assert(err, check.IsNil, check.Commentf(out))
out, err = s.d.Cmd("info")
c.Assert(err, check.IsNil, check.Commentf(out))
err = s.d.Stop()
c.Assert(err, check.IsNil)
// Don't check s.ec.exists, because the daemon no longer calls the
// Exists function.
c.Assert(s.ec.activations, check.Equals, 2)
c.Assert(s.ec.init, check.Equals, 2)
c.Assert(s.ec.creations >= 1, check.Equals, true)
c.Assert(s.ec.removals >= 1, check.Equals, true)
c.Assert(s.ec.gets >= 1, check.Equals, true)
c.Assert(s.ec.puts >= 1, check.Equals, true)
c.Assert(s.ec.stats, check.Equals, 3)
c.Assert(s.ec.cleanups, check.Equals, 2)
c.Assert(s.ec.applydiff >= 1, check.Equals, true)
c.Assert(s.ec.changes, check.Equals, 1)
c.Assert(s.ec.diffsize, check.Equals, 0)
c.Assert(s.ec.diff, check.Equals, 0)
c.Assert(s.ec.metadata, check.Equals, 1)
}
func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriverPull(c *check.C) {
testRequires(c, Network)
c.Assert(s.d.Start(), check.IsNil)
out, err := s.d.Cmd("pull", "busybox:latest")
c.Assert(err, check.IsNil, check.Commentf(out))
out, err = s.d.Cmd("run", "-d", "busybox", "top")
c.Assert(err, check.IsNil, check.Commentf(out))
}