mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	Create extpoint for graphdrivers
Allows people to create out-of-process graphdrivers that can be used with Docker. Extensions must be started before Docker otherwise Docker will fail to start. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
		
							parent
							
								
									9f517fc5bb
								
							
						
					
					
						commit
						b78e4216a2
					
				
					 10 changed files with 967 additions and 17 deletions
				
			
		| 
						 | 
					@ -111,6 +111,9 @@ func GetDriver(name, home string, options []string) (Driver, error) {
 | 
				
			||||||
	if initFunc, exists := drivers[name]; exists {
 | 
						if initFunc, exists := drivers[name]; exists {
 | 
				
			||||||
		return initFunc(filepath.Join(home, name), options)
 | 
							return initFunc(filepath.Join(home, name), options)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if pluginDriver, err := lookupPlugin(name, home, options); err == nil {
 | 
				
			||||||
 | 
							return pluginDriver, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	logrus.Errorf("Failed to GetDriver graph %s %s", name, home)
 | 
						logrus.Errorf("Failed to GetDriver graph %s %s", name, home)
 | 
				
			||||||
	return nil, ErrNotSupported
 | 
						return nil, ErrNotSupported
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										33
									
								
								daemon/graphdriver/plugin.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								daemon/graphdriver/plugin.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					// +build experimental
 | 
				
			||||||
 | 
					// +build daemon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package graphdriver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker/pkg/plugins"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type pluginClient interface {
 | 
				
			||||||
 | 
						// Call calls the specified method with the specified arguments for the plugin.
 | 
				
			||||||
 | 
						Call(string, interface{}, interface{}) error
 | 
				
			||||||
 | 
						// Stream calls the specified method with the specified arguments for the plugin and returns the response IO stream
 | 
				
			||||||
 | 
						Stream(string, interface{}) (io.ReadCloser, error)
 | 
				
			||||||
 | 
						// SendFile calls the specified method, and passes through the IO stream
 | 
				
			||||||
 | 
						SendFile(string, io.Reader, interface{}) error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func lookupPlugin(name, home string, opts []string) (Driver, error) {
 | 
				
			||||||
 | 
						pl, err := plugins.Get(name, "GraphDriver")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("Error looking up graphdriver plugin %s: %v", name, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return newPluginDriver(name, home, opts, pl.Client)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newPluginDriver(name, home string, opts []string, c pluginClient) (Driver, error) {
 | 
				
			||||||
 | 
						proxy := &graphDriverProxy{name, c}
 | 
				
			||||||
 | 
						return proxy, proxy.Init(home, opts)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								daemon/graphdriver/plugin_unsupported.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								daemon/graphdriver/plugin_unsupported.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					// +build !experimental
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package graphdriver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func lookupPlugin(name, home string, opts []string) (Driver, error) {
 | 
				
			||||||
 | 
						return nil, ErrNotSupported
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										210
									
								
								daemon/graphdriver/proxy.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								daemon/graphdriver/proxy.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,210 @@
 | 
				
			||||||
 | 
					// +build experimental
 | 
				
			||||||
 | 
					// +build daemon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package graphdriver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker/pkg/archive"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type graphDriverProxy struct {
 | 
				
			||||||
 | 
						name   string
 | 
				
			||||||
 | 
						client pluginClient
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type graphDriverRequest struct {
 | 
				
			||||||
 | 
						ID         string `json:",omitempty"`
 | 
				
			||||||
 | 
						Parent     string `json:",omitempty"`
 | 
				
			||||||
 | 
						MountLabel string `json:",omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type graphDriverResponse struct {
 | 
				
			||||||
 | 
						Err      string            `json:",omitempty"`
 | 
				
			||||||
 | 
						Dir      string            `json:",omitempty"`
 | 
				
			||||||
 | 
						Exists   bool              `json:",omitempty"`
 | 
				
			||||||
 | 
						Status   [][2]string       `json:",omitempty"`
 | 
				
			||||||
 | 
						Changes  []archive.Change  `json:",omitempty"`
 | 
				
			||||||
 | 
						Size     int64             `json:",omitempty"`
 | 
				
			||||||
 | 
						Metadata map[string]string `json:",omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type graphDriverInitRequest struct {
 | 
				
			||||||
 | 
						Home string
 | 
				
			||||||
 | 
						Opts []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *graphDriverProxy) Init(home string, opts []string) error {
 | 
				
			||||||
 | 
						args := &graphDriverInitRequest{
 | 
				
			||||||
 | 
							Home: home,
 | 
				
			||||||
 | 
							Opts: opts,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var ret graphDriverResponse
 | 
				
			||||||
 | 
						if err := d.client.Call("GraphDriver.Init", args, &ret); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ret.Err != "" {
 | 
				
			||||||
 | 
							return errors.New(ret.Err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *graphDriverProxy) String() string {
 | 
				
			||||||
 | 
						return d.name
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *graphDriverProxy) Create(id, parent string) error {
 | 
				
			||||||
 | 
						args := &graphDriverRequest{
 | 
				
			||||||
 | 
							ID:     id,
 | 
				
			||||||
 | 
							Parent: parent,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var ret graphDriverResponse
 | 
				
			||||||
 | 
						if err := d.client.Call("GraphDriver.Create", args, &ret); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ret.Err != "" {
 | 
				
			||||||
 | 
							return errors.New(ret.Err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *graphDriverProxy) Remove(id string) error {
 | 
				
			||||||
 | 
						args := &graphDriverRequest{ID: id}
 | 
				
			||||||
 | 
						var ret graphDriverResponse
 | 
				
			||||||
 | 
						if err := d.client.Call("GraphDriver.Remove", args, &ret); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ret.Err != "" {
 | 
				
			||||||
 | 
							return errors.New(ret.Err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *graphDriverProxy) Get(id, mountLabel string) (string, error) {
 | 
				
			||||||
 | 
						args := &graphDriverRequest{
 | 
				
			||||||
 | 
							ID:         id,
 | 
				
			||||||
 | 
							MountLabel: mountLabel,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var ret graphDriverResponse
 | 
				
			||||||
 | 
						if err := d.client.Call("GraphDriver.Get", args, &ret); err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						if ret.Err != "" {
 | 
				
			||||||
 | 
							err = errors.New(ret.Err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ret.Dir, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *graphDriverProxy) Put(id string) error {
 | 
				
			||||||
 | 
						args := &graphDriverRequest{ID: id}
 | 
				
			||||||
 | 
						var ret graphDriverResponse
 | 
				
			||||||
 | 
						if err := d.client.Call("GraphDriver.Put", args, &ret); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ret.Err != "" {
 | 
				
			||||||
 | 
							return errors.New(ret.Err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *graphDriverProxy) Exists(id string) bool {
 | 
				
			||||||
 | 
						args := &graphDriverRequest{ID: id}
 | 
				
			||||||
 | 
						var ret graphDriverResponse
 | 
				
			||||||
 | 
						if err := d.client.Call("GraphDriver.Exists", args, &ret); err != nil {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ret.Exists
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *graphDriverProxy) Status() [][2]string {
 | 
				
			||||||
 | 
						args := &graphDriverRequest{}
 | 
				
			||||||
 | 
						var ret graphDriverResponse
 | 
				
			||||||
 | 
						if err := d.client.Call("GraphDriver.Status", args, &ret); err != nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ret.Status
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *graphDriverProxy) GetMetadata(id string) (map[string]string, error) {
 | 
				
			||||||
 | 
						args := &graphDriverRequest{
 | 
				
			||||||
 | 
							ID: id,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var ret graphDriverResponse
 | 
				
			||||||
 | 
						if err := d.client.Call("GraphDriver.GetMetadata", args, &ret); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ret.Err != "" {
 | 
				
			||||||
 | 
							return nil, errors.New(ret.Err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ret.Metadata, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *graphDriverProxy) Cleanup() error {
 | 
				
			||||||
 | 
						args := &graphDriverRequest{}
 | 
				
			||||||
 | 
						var ret graphDriverResponse
 | 
				
			||||||
 | 
						if err := d.client.Call("GraphDriver.Cleanup", args, &ret); err != nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ret.Err != "" {
 | 
				
			||||||
 | 
							return errors.New(ret.Err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *graphDriverProxy) Diff(id, parent string) (archive.Archive, error) {
 | 
				
			||||||
 | 
						args := &graphDriverRequest{
 | 
				
			||||||
 | 
							ID:     id,
 | 
				
			||||||
 | 
							Parent: parent,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						body, err := d.client.Stream("GraphDriver.Diff", args)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							body.Close()
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return archive.Archive(body), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *graphDriverProxy) Changes(id, parent string) ([]archive.Change, error) {
 | 
				
			||||||
 | 
						args := &graphDriverRequest{
 | 
				
			||||||
 | 
							ID:     id,
 | 
				
			||||||
 | 
							Parent: parent,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var ret graphDriverResponse
 | 
				
			||||||
 | 
						if err := d.client.Call("GraphDriver.Changes", args, &ret); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ret.Err != "" {
 | 
				
			||||||
 | 
							return nil, errors.New(ret.Err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret.Changes, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *graphDriverProxy) ApplyDiff(id, parent string, diff archive.Reader) (int64, error) {
 | 
				
			||||||
 | 
						var ret graphDriverResponse
 | 
				
			||||||
 | 
						if err := d.client.SendFile(fmt.Sprintf("GraphDriver.ApplyDiff?id=%s&parent=%s", id, parent), diff, &ret); err != nil {
 | 
				
			||||||
 | 
							return -1, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ret.Err != "" {
 | 
				
			||||||
 | 
							return -1, errors.New(ret.Err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ret.Size, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *graphDriverProxy) DiffSize(id, parent string) (int64, error) {
 | 
				
			||||||
 | 
						args := &graphDriverRequest{
 | 
				
			||||||
 | 
							ID:     id,
 | 
				
			||||||
 | 
							Parent: parent,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var ret graphDriverResponse
 | 
				
			||||||
 | 
						if err := d.client.Call("GraphDriver.DiffSize", args, &ret); err != nil {
 | 
				
			||||||
 | 
							return -1, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ret.Err != "" {
 | 
				
			||||||
 | 
							return -1, errors.New(ret.Err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ret.Size, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										321
									
								
								experimental/plugins_graphdriver.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										321
									
								
								experimental/plugins_graphdriver.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,321 @@
 | 
				
			||||||
 | 
					# Experimental: Docker graph driver plugins
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Docker graph driver plugins enable admins to use an external/out-of-process
 | 
				
			||||||
 | 
					graph driver for use with Docker engine. This is an alternative to using the
 | 
				
			||||||
 | 
					built-in storage drivers, such as aufs/overlay/devicemapper/btrfs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A graph driver plugin is used for image and container fs storage, as such
 | 
				
			||||||
 | 
					the plugin must be started and available for connections prior to Docker Engine
 | 
				
			||||||
 | 
					being started.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Write a graph driver plugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See the [plugin documentation](/docs/extend/plugins.md) for detailed information
 | 
				
			||||||
 | 
					on the underlying plugin protocol.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Graph Driver plugin protocol
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If a plugin registers itself as a `GraphDriver` when activated, then it is
 | 
				
			||||||
 | 
					expected to provide the rootfs for containers as well as image layer storage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### /GraphDriver.Init
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Request**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Home": "/graph/home/path",
 | 
				
			||||||
 | 
					  "Opts": []
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Initialize the graph driver plugin with a home directory and array of options.
 | 
				
			||||||
 | 
					Plugins are not required to accept these options as the Docker Engine does not
 | 
				
			||||||
 | 
					require that the plugin use this path or options, they are only being passed
 | 
				
			||||||
 | 
					through from the user.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Response**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Err": null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Respond with a string error if an error occurred.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### /GraphDriver.Create
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Request**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
 | 
				
			||||||
 | 
					  "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Create a new, empty, filesystem layer with the specified `ID` and `Parent`.
 | 
				
			||||||
 | 
					`Parent` may be an empty string, which would indicate that there is no parent
 | 
				
			||||||
 | 
					layer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Response**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Err: null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Respond with a string error if an error occurred.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### /GraphDriver.Remove
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Request**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Remove the filesystem layer with this given `ID`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Response**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Err: null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Respond with a string error if an error occurred.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### /GraphDriver.Get
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Request**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187"
 | 
				
			||||||
 | 
					  "MountLabel": ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Get the mountpoint for the layered filesystem referred to by the given `ID`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Response**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Dir": "/var/mygraph/46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
 | 
				
			||||||
 | 
					  "Err": ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Respond with the absolute path to the mounted layered filesystem.
 | 
				
			||||||
 | 
					Respond with a string error if an error occurred.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### /GraphDriver.Put
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Request**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Release the system resources for the specified `ID`, such as unmounting the
 | 
				
			||||||
 | 
					filesystem layer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Response**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Err: null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Respond with a string error if an error occurred.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### /GraphDriver.Exists
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Request**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Determine if a filesystem layer with the specified `ID` exists.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Response**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Exists": true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Respond with a boolean for whether or not the filesystem layer with the specified
 | 
				
			||||||
 | 
					`ID` exists.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### /GraphDriver.Status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Request**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Get low-level diagnostic information about the graph driver.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Response**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Status": [[]]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Respond with a 2-D array with key/value pairs for the underlying status
 | 
				
			||||||
 | 
					information.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### /GraphDriver.GetMetadata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Request**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Get low-level diagnostic information about the layered filesystem with the
 | 
				
			||||||
 | 
					with the specified `ID`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Response**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Metadata": {},
 | 
				
			||||||
 | 
					  "Err": null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Respond with a set of key/value pairs containing the low-level diagnostic
 | 
				
			||||||
 | 
					information about the layered filesystem.
 | 
				
			||||||
 | 
					Respond with a string error if an error occurred.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### /GraphDriver.Cleanup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Request**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Perform neccessary tasks to release resources help by the plugin, for example
 | 
				
			||||||
 | 
					unmounting all the layered file systems.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Response**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Err: null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Respond with a string error if an error occurred.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### /GraphDriver.Diff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Request**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
 | 
				
			||||||
 | 
					  "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Get an archive of the changes between the filesystem layers specified by the `ID`
 | 
				
			||||||
 | 
					and `Parent`. `Parent` may be an empty string, in which case there is no parent.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Response**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{{ TAR STREAM }}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### /GraphDriver.Changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Request**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
 | 
				
			||||||
 | 
					  "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Get a list of changes between the filesystem layers specified by the `ID` and
 | 
				
			||||||
 | 
					`Parent`. `Parent` may be an empty string, in which case there is no parent.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Response**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Changes": [{}],
 | 
				
			||||||
 | 
					  "Err": null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Responds with a list of changes. The structure of a change is:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					  "Path": "/some/path",
 | 
				
			||||||
 | 
					  "Kind": 0,
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Where teh `Path` is the filesystem path within the layered filesystem that is
 | 
				
			||||||
 | 
					changed and `Kind` is an integer specifying the type of change that occurred:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- 0 - Modified
 | 
				
			||||||
 | 
					- 1 - Added
 | 
				
			||||||
 | 
					- 2 - Deleted
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Respond with a string error if an error occurred.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### /GraphDriver.ApplyDiff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Request**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{{ TAR STREAM }}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Extract the changeset from the given diff into the layer with the specified `ID`
 | 
				
			||||||
 | 
					and `Parent`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Query Parameters**:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- id (required)- the `ID` of the new filesystem layer to extract the diff to
 | 
				
			||||||
 | 
					- parent (required)- the `Parent` of the given `ID`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Response**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Size": 512366,
 | 
				
			||||||
 | 
					  "Err": null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Respond with the size of the new layer in bytes.
 | 
				
			||||||
 | 
					Respond with a string error if an error occurred.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### /GraphDriver.DiffSize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Request**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "ID": "46fe8644f2572fd1e505364f7581e0c9dbc7f14640bd1fb6ce97714fb6fc5187",
 | 
				
			||||||
 | 
					  "Parent": "2cd9c322cb78a55e8212aa3ea8425a4180236d7106938ec921d0935a4b8ca142"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Calculate the changes between the specified `ID`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Response**:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "Size": 512366,
 | 
				
			||||||
 | 
					  "Err": null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Respond with the size changes between the specified `ID` and `Parent`
 | 
				
			||||||
 | 
					Respond with a string error if an error occurred.
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,22 @@
 | 
				
			||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker/pkg/reexec"
 | 
				
			||||||
	"github.com/go-check/check"
 | 
						"github.com/go-check/check"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Test(t *testing.T) {
 | 
					func Test(t *testing.T) {
 | 
				
			||||||
 | 
						reexec.Init() // This is required for external graphdriver tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !isLocalDaemon {
 | 
				
			||||||
 | 
							fmt.Println("INFO: Testing against a remote daemon")
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							fmt.Println("INFO: Testing against a local daemon")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	check.TestingT(t)
 | 
						check.TestingT(t)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										348
									
								
								integration-cli/docker_cli_external_graphdriver_unix_test.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										348
									
								
								integration-cli/docker_cli_external_graphdriver_unix_test.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,348 @@
 | 
				
			||||||
 | 
					// +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": %s}`, t.Error()))
 | 
				
			||||||
 | 
							case string:
 | 
				
			||||||
 | 
								fmt.Fprintln(w, t)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								json.NewEncoder(w).Encode(&data)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						base, err := ioutil.TempDir("", "external-graph-test")
 | 
				
			||||||
 | 
						c.Assert(err, check.IsNil)
 | 
				
			||||||
 | 
						vfsProto, err := vfs.Init(base, []string{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							c.Fatalf("error initializing graph driver: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						driver := graphdriver.NaiveDiffDriver(vfsProto)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 := json.NewDecoder(r.Body).Decode(&req); err != nil {
 | 
				
			||||||
 | 
								http.Error(w, err.Error(), 500)
 | 
				
			||||||
 | 
								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 := json.NewDecoder(r.Body).Decode(&req); err != nil {
 | 
				
			||||||
 | 
								http.Error(w, err.Error(), 500)
 | 
				
			||||||
 | 
								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 := json.NewDecoder(r.Body).Decode(&req); err != nil {
 | 
				
			||||||
 | 
								http.Error(w, err.Error(), 500)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							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 := json.NewDecoder(r.Body).Decode(&req); err != nil {
 | 
				
			||||||
 | 
								http.Error(w, err.Error(), 500)
 | 
				
			||||||
 | 
								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 := json.NewDecoder(r.Body).Decode(&req); err != nil {
 | 
				
			||||||
 | 
								http.Error(w, err.Error(), 500)
 | 
				
			||||||
 | 
								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, `{"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 := json.NewDecoder(r.Body).Decode(&req); err != nil {
 | 
				
			||||||
 | 
								http.Error(w, err.Error(), 500)
 | 
				
			||||||
 | 
								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 := json.NewDecoder(r.Body).Decode(&req); err != nil {
 | 
				
			||||||
 | 
								http.Error(w, err.Error(), 500)
 | 
				
			||||||
 | 
								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 := json.NewDecoder(r.Body).Decode(&req); err != nil {
 | 
				
			||||||
 | 
								http.Error(w, err.Error(), 500)
 | 
				
			||||||
 | 
								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++
 | 
				
			||||||
 | 
							id := r.URL.Query().Get("id")
 | 
				
			||||||
 | 
							parent := r.URL.Query().Get("parent")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							size, err := driver.ApplyDiff(id, parent, r.Body)
 | 
				
			||||||
 | 
							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 := json.NewDecoder(r.Body).Decode(&req); err != nil {
 | 
				
			||||||
 | 
								http.Error(w, err.Error(), 500)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							size, err := driver.DiffSize(req.ID, req.Parent)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								respond(w, err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							respond(w, &graphDriverResponse{Size: size})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := os.MkdirAll("/etc/docker/plugins", 0755); err != nil {
 | 
				
			||||||
 | 
							c.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := ioutil.WriteFile("/etc/docker/plugins/test-external-graph-driver.spec", []byte(s.server.URL), 0644); err != nil {
 | 
				
			||||||
 | 
							c.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *DockerExternalGraphdriverSuite) TearDownSuite(c *check.C) {
 | 
				
			||||||
 | 
						s.server.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := os.RemoveAll("/etc/docker/plugins"); err != nil {
 | 
				
			||||||
 | 
							c.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriver(c *check.C) {
 | 
				
			||||||
 | 
						c.Assert(s.d.StartWithBusybox("-s", "test-external-graph-driver"), check.IsNil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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, 1)
 | 
				
			||||||
 | 
						c.Assert(s.ec.cleanups, check.Equals, 2)
 | 
				
			||||||
 | 
						c.Assert(s.ec.exists >= 1, check.Equals, true)
 | 
				
			||||||
 | 
						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))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -68,11 +68,8 @@ func init() {
 | 
				
			||||||
	// Similarly, it will be perfectly valid to also run CLI tests from
 | 
						// Similarly, it will be perfectly valid to also run CLI tests from
 | 
				
			||||||
	// a Linux CLI (built with the daemon tag) against a Windows daemon.
 | 
						// a Linux CLI (built with the daemon tag) against a Windows daemon.
 | 
				
			||||||
	if len(os.Getenv("DOCKER_REMOTE_DAEMON")) > 0 {
 | 
						if len(os.Getenv("DOCKER_REMOTE_DAEMON")) > 0 {
 | 
				
			||||||
		fmt.Println("INFO: Testing against a remote daemon")
 | 
					 | 
				
			||||||
		isLocalDaemon = false
 | 
							isLocalDaemon = false
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		fmt.Println("INFO: Testing against a local daemon")
 | 
					 | 
				
			||||||
		isLocalDaemon = true
 | 
							isLocalDaemon = true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
| 
						 | 
					@ -52,19 +53,41 @@ type Client struct {
 | 
				
			||||||
// Call calls the specified method with the specified arguments for the plugin.
 | 
					// Call calls the specified method with the specified arguments for the plugin.
 | 
				
			||||||
// It will retry for 30 seconds if a failure occurs when calling.
 | 
					// It will retry for 30 seconds if a failure occurs when calling.
 | 
				
			||||||
func (c *Client) Call(serviceMethod string, args interface{}, ret interface{}) error {
 | 
					func (c *Client) Call(serviceMethod string, args interface{}, ret interface{}) error {
 | 
				
			||||||
	return c.callWithRetry(serviceMethod, args, ret, true)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *Client) callWithRetry(serviceMethod string, args interface{}, ret interface{}, retry bool) error {
 | 
					 | 
				
			||||||
	var buf bytes.Buffer
 | 
						var buf bytes.Buffer
 | 
				
			||||||
	if err := json.NewEncoder(&buf).Encode(args); err != nil {
 | 
						if err := json.NewEncoder(&buf).Encode(args); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						body, err := c.callWithRetry(serviceMethod, &buf, true)
 | 
				
			||||||
	req, err := http.NewRequest("POST", "/"+serviceMethod, &buf)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						defer body.Close()
 | 
				
			||||||
 | 
						return json.NewDecoder(body).Decode(&ret)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Stream calls the specified method with the specified arguments for the plugin and returns the response body
 | 
				
			||||||
 | 
					func (c *Client) Stream(serviceMethod string, args interface{}) (io.ReadCloser, error) {
 | 
				
			||||||
 | 
						var buf bytes.Buffer
 | 
				
			||||||
 | 
						if err := json.NewEncoder(&buf).Encode(args); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return c.callWithRetry(serviceMethod, &buf, true)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SendFile calls the specified method, and passes through the IO stream
 | 
				
			||||||
 | 
					func (c *Client) SendFile(serviceMethod string, data io.Reader, ret interface{}) error {
 | 
				
			||||||
 | 
						body, err := c.callWithRetry(serviceMethod, data, true)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return json.NewDecoder(body).Decode(&ret)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Client) callWithRetry(serviceMethod string, data io.Reader, retry bool) (io.ReadCloser, error) {
 | 
				
			||||||
 | 
						req, err := http.NewRequest("POST", "/"+serviceMethod, data)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	req.Header.Add("Accept", versionMimetype)
 | 
						req.Header.Add("Accept", versionMimetype)
 | 
				
			||||||
	req.URL.Scheme = "http"
 | 
						req.URL.Scheme = "http"
 | 
				
			||||||
	req.URL.Host = c.addr
 | 
						req.URL.Host = c.addr
 | 
				
			||||||
| 
						 | 
					@ -76,12 +99,12 @@ func (c *Client) callWithRetry(serviceMethod string, args interface{}, ret inter
 | 
				
			||||||
		resp, err := c.http.Do(req)
 | 
							resp, err := c.http.Do(req)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			if !retry {
 | 
								if !retry {
 | 
				
			||||||
				return err
 | 
									return nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			timeOff := backoff(retries)
 | 
								timeOff := backoff(retries)
 | 
				
			||||||
			if abort(start, timeOff) {
 | 
								if abort(start, timeOff) {
 | 
				
			||||||
				return err
 | 
									return nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			retries++
 | 
								retries++
 | 
				
			||||||
			logrus.Warnf("Unable to connect to plugin: %s, retrying in %v", c.addr, timeOff)
 | 
								logrus.Warnf("Unable to connect to plugin: %s, retrying in %v", c.addr, timeOff)
 | 
				
			||||||
| 
						 | 
					@ -89,16 +112,14 @@ func (c *Client) callWithRetry(serviceMethod string, args interface{}, ret inter
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		defer resp.Body.Close()
 | 
					 | 
				
			||||||
		if resp.StatusCode != http.StatusOK {
 | 
							if resp.StatusCode != http.StatusOK {
 | 
				
			||||||
			remoteErr, err := ioutil.ReadAll(resp.Body)
 | 
								remoteErr, err := ioutil.ReadAll(resp.Body)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return &remoteError{err.Error(), serviceMethod}
 | 
									return nil, &remoteError{err.Error(), serviceMethod}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return &remoteError{string(remoteErr), serviceMethod}
 | 
								return nil, &remoteError{string(remoteErr), serviceMethod}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							return resp.Body, nil
 | 
				
			||||||
		return json.NewDecoder(resp.Body).Decode(&ret)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,7 +30,7 @@ func teardownRemotePluginServer() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestFailedConnection(t *testing.T) {
 | 
					func TestFailedConnection(t *testing.T) {
 | 
				
			||||||
	c, _ := NewClient("tcp://127.0.0.1:1", tlsconfig.Options{InsecureSkipVerify: true})
 | 
						c, _ := NewClient("tcp://127.0.0.1:1", tlsconfig.Options{InsecureSkipVerify: true})
 | 
				
			||||||
	err := c.callWithRetry("Service.Method", nil, nil, false)
 | 
						_, err := c.callWithRetry("Service.Method", nil, false)
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		t.Fatal("Unexpected successful connection")
 | 
							t.Fatal("Unexpected successful connection")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue