github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/plugin/v2/plugin_experimental.go (about)

     1  // +build experimental
     2  
     3  package v2
     4  
     5  import (
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  
    13  	"github.com/docker/docker/api/types"
    14  	"github.com/docker/docker/pkg/plugins"
    15  	"github.com/docker/docker/pkg/system"
    16  	"github.com/opencontainers/runtime-spec/specs-go"
    17  )
    18  
    19  const defaultPluginRuntimeDestination = "/run/docker/plugins"
    20  
    21  // ErrInadequateCapability indicates that the plugin did not have the requested capability.
    22  type ErrInadequateCapability string
    23  
    24  func (cap ErrInadequateCapability) Error() string {
    25  	return fmt.Sprintf("plugin does not provide %q capability", cap)
    26  }
    27  
    28  func newPluginObj(name, id, tag string) types.Plugin {
    29  	return types.Plugin{Name: name, ID: id, Tag: tag}
    30  }
    31  
    32  // NewPlugin creates a plugin.
    33  func NewPlugin(name, id, runRoot, tag string) *Plugin {
    34  	return &Plugin{
    35  		PluginObj:         newPluginObj(name, id, tag),
    36  		RuntimeSourcePath: filepath.Join(runRoot, id),
    37  	}
    38  }
    39  
    40  // Client returns the plugin client.
    41  func (p *Plugin) Client() *plugins.Client {
    42  	return p.PClient
    43  }
    44  
    45  // IsV1 returns true for V1 plugins and false otherwise.
    46  func (p *Plugin) IsV1() bool {
    47  	return false
    48  }
    49  
    50  // Name returns the plugin name.
    51  func (p *Plugin) Name() string {
    52  	name := p.PluginObj.Name
    53  	if len(p.PluginObj.Tag) > 0 {
    54  		// TODO: this feels hacky, maybe we should be storing the distribution reference rather than splitting these
    55  		name += ":" + p.PluginObj.Tag
    56  	}
    57  	return name
    58  }
    59  
    60  // FilterByCap query the plugin for a given capability.
    61  func (p *Plugin) FilterByCap(capability string) (*Plugin, error) {
    62  	capability = strings.ToLower(capability)
    63  	for _, typ := range p.PluginObj.Manifest.Interface.Types {
    64  		if typ.Capability == capability && typ.Prefix == "docker" {
    65  			return p, nil
    66  		}
    67  	}
    68  	return nil, ErrInadequateCapability(capability)
    69  }
    70  
    71  // RemoveFromDisk deletes the plugin's runtime files from disk.
    72  func (p *Plugin) RemoveFromDisk() error {
    73  	return os.RemoveAll(p.RuntimeSourcePath)
    74  }
    75  
    76  // InitPlugin populates the plugin object from the plugin manifest file.
    77  func (p *Plugin) InitPlugin(libRoot string) error {
    78  	dt, err := os.Open(filepath.Join(libRoot, p.PluginObj.ID, "manifest.json"))
    79  	if err != nil {
    80  		return err
    81  	}
    82  	err = json.NewDecoder(dt).Decode(&p.PluginObj.Manifest)
    83  	dt.Close()
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	p.PluginObj.Config.Mounts = make([]types.PluginMount, len(p.PluginObj.Manifest.Mounts))
    89  	for i, mount := range p.PluginObj.Manifest.Mounts {
    90  		p.PluginObj.Config.Mounts[i] = mount
    91  	}
    92  	p.PluginObj.Config.Env = make([]string, 0, len(p.PluginObj.Manifest.Env))
    93  	for _, env := range p.PluginObj.Manifest.Env {
    94  		if env.Value != nil {
    95  			p.PluginObj.Config.Env = append(p.PluginObj.Config.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
    96  		}
    97  	}
    98  	copy(p.PluginObj.Config.Args, p.PluginObj.Manifest.Args.Value)
    99  
   100  	f, err := os.Create(filepath.Join(libRoot, p.PluginObj.ID, "plugin-config.json"))
   101  	if err != nil {
   102  		return err
   103  	}
   104  	err = json.NewEncoder(f).Encode(&p.PluginObj.Config)
   105  	f.Close()
   106  	return err
   107  }
   108  
   109  // Set is used to pass arguments to the plugin.
   110  func (p *Plugin) Set(args []string) error {
   111  	m := make(map[string]string, len(args))
   112  	for _, arg := range args {
   113  		i := strings.Index(arg, "=")
   114  		if i < 0 {
   115  			return fmt.Errorf("No equal sign '=' found in %s", arg)
   116  		}
   117  		m[arg[:i]] = arg[i+1:]
   118  	}
   119  	return errors.New("not implemented")
   120  }
   121  
   122  // ComputePrivileges takes the manifest file and computes the list of access necessary
   123  // for the plugin on the host.
   124  func (p *Plugin) ComputePrivileges() types.PluginPrivileges {
   125  	m := p.PluginObj.Manifest
   126  	var privileges types.PluginPrivileges
   127  	if m.Network.Type != "null" && m.Network.Type != "bridge" {
   128  		privileges = append(privileges, types.PluginPrivilege{
   129  			Name:        "network",
   130  			Description: "",
   131  			Value:       []string{m.Network.Type},
   132  		})
   133  	}
   134  	for _, mount := range m.Mounts {
   135  		if mount.Source != nil {
   136  			privileges = append(privileges, types.PluginPrivilege{
   137  				Name:        "mount",
   138  				Description: "",
   139  				Value:       []string{*mount.Source},
   140  			})
   141  		}
   142  	}
   143  	for _, device := range m.Devices {
   144  		if device.Path != nil {
   145  			privileges = append(privileges, types.PluginPrivilege{
   146  				Name:        "device",
   147  				Description: "",
   148  				Value:       []string{*device.Path},
   149  			})
   150  		}
   151  	}
   152  	if len(m.Capabilities) > 0 {
   153  		privileges = append(privileges, types.PluginPrivilege{
   154  			Name:        "capabilities",
   155  			Description: "",
   156  			Value:       m.Capabilities,
   157  		})
   158  	}
   159  	return privileges
   160  }
   161  
   162  // IsEnabled returns the active state of the plugin.
   163  func (p *Plugin) IsEnabled() bool {
   164  	p.RLock()
   165  	defer p.RUnlock()
   166  
   167  	return p.PluginObj.Enabled
   168  }
   169  
   170  // GetID returns the plugin's ID.
   171  func (p *Plugin) GetID() string {
   172  	p.RLock()
   173  	defer p.RUnlock()
   174  
   175  	return p.PluginObj.ID
   176  }
   177  
   178  // GetSocket returns the plugin socket.
   179  func (p *Plugin) GetSocket() string {
   180  	p.RLock()
   181  	defer p.RUnlock()
   182  
   183  	return p.PluginObj.Manifest.Interface.Socket
   184  }
   185  
   186  // GetTypes returns the interface types of a plugin.
   187  func (p *Plugin) GetTypes() []types.PluginInterfaceType {
   188  	p.RLock()
   189  	defer p.RUnlock()
   190  
   191  	return p.PluginObj.Manifest.Interface.Types
   192  }
   193  
   194  // InitSpec creates an OCI spec from the plugin's config.
   195  func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
   196  	rootfs := filepath.Join(libRoot, p.PluginObj.ID, "rootfs")
   197  	s.Root = specs.Root{
   198  		Path:     rootfs,
   199  		Readonly: false, // TODO: all plugins should be readonly? settable in manifest?
   200  	}
   201  
   202  	mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
   203  		Source:      &p.RuntimeSourcePath,
   204  		Destination: defaultPluginRuntimeDestination,
   205  		Type:        "bind",
   206  		Options:     []string{"rbind", "rshared"},
   207  	})
   208  	for _, mount := range mounts {
   209  		m := specs.Mount{
   210  			Destination: mount.Destination,
   211  			Type:        mount.Type,
   212  			Options:     mount.Options,
   213  		}
   214  		// TODO: if nil, then it's required and user didn't set it
   215  		if mount.Source != nil {
   216  			m.Source = *mount.Source
   217  		}
   218  		if m.Source != "" && m.Type == "bind" {
   219  			fi, err := os.Lstat(filepath.Join(rootfs, m.Destination)) // TODO: followsymlinks
   220  			if err != nil {
   221  				return nil, err
   222  			}
   223  			if fi.IsDir() {
   224  				if err := os.MkdirAll(m.Source, 0700); err != nil {
   225  					return nil, err
   226  				}
   227  			}
   228  		}
   229  		s.Mounts = append(s.Mounts, m)
   230  	}
   231  
   232  	envs := make([]string, 1, len(p.PluginObj.Config.Env)+1)
   233  	envs[0] = "PATH=" + system.DefaultPathEnv
   234  	envs = append(envs, p.PluginObj.Config.Env...)
   235  
   236  	args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...)
   237  	cwd := p.PluginObj.Manifest.Workdir
   238  	if len(cwd) == 0 {
   239  		cwd = "/"
   240  	}
   241  	s.Process = specs.Process{
   242  		Terminal: false,
   243  		Args:     args,
   244  		Cwd:      cwd,
   245  		Env:      envs,
   246  	}
   247  
   248  	return &s, nil
   249  }