123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- package v2
- import (
- "encoding/json"
- "errors"
- "fmt"
- "os"
- "path/filepath"
- "strings"
- "sync"
- "github.com/docker/docker/api/types"
- "github.com/docker/docker/pkg/plugins"
- "github.com/docker/docker/pkg/system"
- specs "github.com/opencontainers/runtime-spec/specs-go"
- )
- // Plugin represents an individual plugin.
- type Plugin struct {
- sync.RWMutex
- PluginObj types.Plugin `json:"plugin"`
- PClient *plugins.Client `json:"-"`
- RuntimeSourcePath string `json:"-"`
- RefCount int `json:"-"`
- Restart bool `json:"-"`
- ExitChan chan bool `json:"-"`
- }
- const defaultPluginRuntimeDestination = "/run/docker/plugins"
- // ErrInadequateCapability indicates that the plugin did not have the requested capability.
- type ErrInadequateCapability struct {
- cap string
- }
- func (e ErrInadequateCapability) Error() string {
- return fmt.Sprintf("plugin does not provide %q capability", e.cap)
- }
- func newPluginObj(name, id, tag string) types.Plugin {
- return types.Plugin{Name: name, ID: id, Tag: tag}
- }
- // NewPlugin creates a plugin.
- func NewPlugin(name, id, runRoot, tag string) *Plugin {
- return &Plugin{
- PluginObj: newPluginObj(name, id, tag),
- RuntimeSourcePath: filepath.Join(runRoot, id),
- }
- }
- // Client returns the plugin client.
- func (p *Plugin) Client() *plugins.Client {
- return p.PClient
- }
- // IsV1 returns true for V1 plugins and false otherwise.
- func (p *Plugin) IsV1() bool {
- return false
- }
- // Name returns the plugin name.
- func (p *Plugin) Name() string {
- name := p.PluginObj.Name
- if len(p.PluginObj.Tag) > 0 {
- // TODO: this feels hacky, maybe we should be storing the distribution reference rather than splitting these
- name += ":" + p.PluginObj.Tag
- }
- return name
- }
- // FilterByCap query the plugin for a given capability.
- func (p *Plugin) FilterByCap(capability string) (*Plugin, error) {
- capability = strings.ToLower(capability)
- for _, typ := range p.PluginObj.Manifest.Interface.Types {
- if typ.Capability == capability && typ.Prefix == "docker" {
- return p, nil
- }
- }
- return nil, ErrInadequateCapability{capability}
- }
- // RemoveFromDisk deletes the plugin's runtime files from disk.
- func (p *Plugin) RemoveFromDisk() error {
- return os.RemoveAll(p.RuntimeSourcePath)
- }
- // InitPlugin populates the plugin object from the plugin manifest file.
- func (p *Plugin) InitPlugin(libRoot string) error {
- dt, err := os.Open(filepath.Join(libRoot, p.PluginObj.ID, "manifest.json"))
- if err != nil {
- return err
- }
- err = json.NewDecoder(dt).Decode(&p.PluginObj.Manifest)
- dt.Close()
- if err != nil {
- return err
- }
- p.PluginObj.Config.Mounts = make([]types.PluginMount, len(p.PluginObj.Manifest.Mounts))
- for i, mount := range p.PluginObj.Manifest.Mounts {
- p.PluginObj.Config.Mounts[i] = mount
- }
- p.PluginObj.Config.Env = make([]string, 0, len(p.PluginObj.Manifest.Env))
- for _, env := range p.PluginObj.Manifest.Env {
- if env.Value != nil {
- p.PluginObj.Config.Env = append(p.PluginObj.Config.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
- }
- }
- copy(p.PluginObj.Config.Args, p.PluginObj.Manifest.Args.Value)
- f, err := os.Create(filepath.Join(libRoot, p.PluginObj.ID, "plugin-config.json"))
- if err != nil {
- return err
- }
- err = json.NewEncoder(f).Encode(&p.PluginObj.Config)
- f.Close()
- return err
- }
- // Set is used to pass arguments to the plugin.
- func (p *Plugin) Set(args []string) error {
- m := make(map[string]string, len(args))
- for _, arg := range args {
- i := strings.Index(arg, "=")
- if i < 0 {
- return fmt.Errorf("No equal sign '=' found in %s", arg)
- }
- m[arg[:i]] = arg[i+1:]
- }
- return errors.New("not implemented")
- }
- // ComputePrivileges takes the manifest file and computes the list of access necessary
- // for the plugin on the host.
- func (p *Plugin) ComputePrivileges() types.PluginPrivileges {
- m := p.PluginObj.Manifest
- var privileges types.PluginPrivileges
- if m.Network.Type != "null" && m.Network.Type != "bridge" {
- privileges = append(privileges, types.PluginPrivilege{
- Name: "network",
- Description: "",
- Value: []string{m.Network.Type},
- })
- }
- for _, mount := range m.Mounts {
- if mount.Source != nil {
- privileges = append(privileges, types.PluginPrivilege{
- Name: "mount",
- Description: "",
- Value: []string{*mount.Source},
- })
- }
- }
- for _, device := range m.Devices {
- if device.Path != nil {
- privileges = append(privileges, types.PluginPrivilege{
- Name: "device",
- Description: "",
- Value: []string{*device.Path},
- })
- }
- }
- if len(m.Capabilities) > 0 {
- privileges = append(privileges, types.PluginPrivilege{
- Name: "capabilities",
- Description: "",
- Value: m.Capabilities,
- })
- }
- return privileges
- }
- // IsEnabled returns the active state of the plugin.
- func (p *Plugin) IsEnabled() bool {
- p.RLock()
- defer p.RUnlock()
- return p.PluginObj.Enabled
- }
- // GetID returns the plugin's ID.
- func (p *Plugin) GetID() string {
- p.RLock()
- defer p.RUnlock()
- return p.PluginObj.ID
- }
- // GetSocket returns the plugin socket.
- func (p *Plugin) GetSocket() string {
- p.RLock()
- defer p.RUnlock()
- return p.PluginObj.Manifest.Interface.Socket
- }
- // GetTypes returns the interface types of a plugin.
- func (p *Plugin) GetTypes() []types.PluginInterfaceType {
- p.RLock()
- defer p.RUnlock()
- return p.PluginObj.Manifest.Interface.Types
- }
- // InitSpec creates an OCI spec from the plugin's config.
- func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
- rootfs := filepath.Join(libRoot, p.PluginObj.ID, "rootfs")
- s.Root = specs.Root{
- Path: rootfs,
- Readonly: false, // TODO: all plugins should be readonly? settable in manifest?
- }
- mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
- Source: &p.RuntimeSourcePath,
- Destination: defaultPluginRuntimeDestination,
- Type: "bind",
- Options: []string{"rbind", "rshared"},
- })
- for _, mount := range mounts {
- m := specs.Mount{
- Destination: mount.Destination,
- Type: mount.Type,
- Options: mount.Options,
- }
- // TODO: if nil, then it's required and user didn't set it
- if mount.Source != nil {
- m.Source = *mount.Source
- }
- if m.Source != "" && m.Type == "bind" {
- fi, err := os.Lstat(filepath.Join(rootfs, m.Destination)) // TODO: followsymlinks
- if err != nil {
- return nil, err
- }
- if fi.IsDir() {
- if err := os.MkdirAll(m.Source, 0700); err != nil {
- return nil, err
- }
- }
- }
- s.Mounts = append(s.Mounts, m)
- }
- envs := make([]string, 1, len(p.PluginObj.Config.Env)+1)
- envs[0] = "PATH=" + system.DefaultPathEnv
- envs = append(envs, p.PluginObj.Config.Env...)
- args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...)
- cwd := p.PluginObj.Manifest.Workdir
- if len(cwd) == 0 {
- cwd = "/"
- }
- s.Process = specs.Process{
- Terminal: false,
- Args: args,
- Cwd: cwd,
- Env: envs,
- }
- return &s, nil
- }
|