123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- package v2 // import "github.com/docker/docker/plugin/v2"
- import (
- "fmt"
- "net"
- "path/filepath"
- "strings"
- "sync"
- "time"
- "github.com/docker/docker/api/types"
- "github.com/docker/docker/pkg/plugingetter"
- "github.com/docker/docker/pkg/plugins"
- "github.com/opencontainers/go-digest"
- specs "github.com/opencontainers/runtime-spec/specs-go"
- )
- // Plugin represents an individual plugin.
- type Plugin struct {
- mu sync.RWMutex
- PluginObj types.Plugin `json:"plugin"` // todo: embed struct
- pClient *plugins.Client
- refCount int
- Rootfs string // TODO: make private
- Config digest.Digest
- Blobsums []digest.Digest
- Manifest digest.Digest
- modifyRuntimeSpec func(*specs.Spec)
- SwarmServiceID string
- timeout time.Duration
- addr net.Addr
- }
- 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)
- }
- // ScopedPath returns the path scoped to the plugin rootfs
- func (p *Plugin) ScopedPath(s string) string {
- if p.PluginObj.Config.PropagatedMount != "" && strings.HasPrefix(s, p.PluginObj.Config.PropagatedMount) {
- // re-scope to the propagated mount path on the host
- return filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount", strings.TrimPrefix(s, p.PluginObj.Config.PropagatedMount))
- }
- return filepath.Join(p.Rootfs, s)
- }
- // Client returns the plugin client.
- // Deprecated: use p.Addr() and manually create the client
- func (p *Plugin) Client() *plugins.Client {
- p.mu.RLock()
- defer p.mu.RUnlock()
- return p.pClient
- }
- // SetPClient set the plugin client.
- // Deprecated: Hardcoded plugin client is deprecated
- func (p *Plugin) SetPClient(client *plugins.Client) {
- p.mu.Lock()
- defer p.mu.Unlock()
- p.pClient = client
- }
- // 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 {
- return p.PluginObj.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.Config.Interface.Types {
- if typ.Capability == capability && typ.Prefix == "docker" {
- return p, nil
- }
- }
- return nil, ErrInadequateCapability{capability}
- }
- // InitEmptySettings initializes empty settings for a plugin.
- func (p *Plugin) InitEmptySettings() {
- p.PluginObj.Settings.Mounts = make([]types.PluginMount, len(p.PluginObj.Config.Mounts))
- copy(p.PluginObj.Settings.Mounts, p.PluginObj.Config.Mounts)
- p.PluginObj.Settings.Devices = make([]types.PluginDevice, len(p.PluginObj.Config.Linux.Devices))
- copy(p.PluginObj.Settings.Devices, p.PluginObj.Config.Linux.Devices)
- p.PluginObj.Settings.Env = make([]string, 0, len(p.PluginObj.Config.Env))
- for _, env := range p.PluginObj.Config.Env {
- if env.Value != nil {
- p.PluginObj.Settings.Env = append(p.PluginObj.Settings.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
- }
- }
- p.PluginObj.Settings.Args = make([]string, len(p.PluginObj.Config.Args.Value))
- copy(p.PluginObj.Settings.Args, p.PluginObj.Config.Args.Value)
- }
- // Set is used to pass arguments to the plugin.
- func (p *Plugin) Set(args []string) error {
- p.mu.Lock()
- defer p.mu.Unlock()
- if p.PluginObj.Enabled {
- return fmt.Errorf("cannot set on an active plugin, disable plugin before setting")
- }
- sets, err := newSettables(args)
- if err != nil {
- return err
- }
- // TODO(vieux): lots of code duplication here, needs to be refactored.
- next:
- for _, set := range sets {
- s := set
- // range over all the envs in the config
- for _, env := range p.PluginObj.Config.Env {
- // found the env in the config
- if env.Name == s.name {
- // is it settable ?
- if ok, err := s.isSettable(allowedSettableFieldsEnv, env.Settable); err != nil {
- return err
- } else if !ok {
- return fmt.Errorf("%q is not settable", s.prettyName())
- }
- // is it, so lets update the settings in memory
- updateSettingsEnv(&p.PluginObj.Settings.Env, &s)
- continue next
- }
- }
- // range over all the mounts in the config
- for _, mount := range p.PluginObj.Config.Mounts {
- // found the mount in the config
- if mount.Name == s.name {
- // is it settable ?
- if ok, err := s.isSettable(allowedSettableFieldsMounts, mount.Settable); err != nil {
- return err
- } else if !ok {
- return fmt.Errorf("%q is not settable", s.prettyName())
- }
- // it is, so lets update the settings in memory
- if mount.Source == nil {
- return fmt.Errorf("Plugin config has no mount source")
- }
- *mount.Source = s.value
- continue next
- }
- }
- // range over all the devices in the config
- for _, device := range p.PluginObj.Config.Linux.Devices {
- // found the device in the config
- if device.Name == s.name {
- // is it settable ?
- if ok, err := s.isSettable(allowedSettableFieldsDevices, device.Settable); err != nil {
- return err
- } else if !ok {
- return fmt.Errorf("%q is not settable", s.prettyName())
- }
- // it is, so lets update the settings in memory
- if device.Path == nil {
- return fmt.Errorf("Plugin config has no device path")
- }
- *device.Path = s.value
- continue next
- }
- }
- // found the name in the config
- if p.PluginObj.Config.Args.Name == s.name {
- // is it settable ?
- if ok, err := s.isSettable(allowedSettableFieldsArgs, p.PluginObj.Config.Args.Settable); err != nil {
- return err
- } else if !ok {
- return fmt.Errorf("%q is not settable", s.prettyName())
- }
- // it is, so lets update the settings in memory
- p.PluginObj.Settings.Args = strings.Split(s.value, " ")
- continue next
- }
- return fmt.Errorf("setting %q not found in the plugin configuration", s.name)
- }
- return nil
- }
- // IsEnabled returns the active state of the plugin.
- func (p *Plugin) IsEnabled() bool {
- p.mu.RLock()
- defer p.mu.RUnlock()
- return p.PluginObj.Enabled
- }
- // GetID returns the plugin's ID.
- func (p *Plugin) GetID() string {
- p.mu.RLock()
- defer p.mu.RUnlock()
- return p.PluginObj.ID
- }
- // GetSocket returns the plugin socket.
- func (p *Plugin) GetSocket() string {
- p.mu.RLock()
- defer p.mu.RUnlock()
- return p.PluginObj.Config.Interface.Socket
- }
- // GetTypes returns the interface types of a plugin.
- func (p *Plugin) GetTypes() []types.PluginInterfaceType {
- p.mu.RLock()
- defer p.mu.RUnlock()
- return p.PluginObj.Config.Interface.Types
- }
- // GetRefCount returns the reference count.
- func (p *Plugin) GetRefCount() int {
- p.mu.RLock()
- defer p.mu.RUnlock()
- return p.refCount
- }
- // AddRefCount adds to reference count.
- func (p *Plugin) AddRefCount(count int) {
- p.mu.Lock()
- defer p.mu.Unlock()
- p.refCount += count
- }
- // Acquire increments the plugin's reference count
- // This should be followed up by `Release()` when the plugin is no longer in use.
- func (p *Plugin) Acquire() {
- p.AddRefCount(plugingetter.Acquire)
- }
- // Release decrements the plugin's reference count
- // This should only be called when the plugin is no longer in use, e.g. with
- // via `Acquire()` or getter.Get("name", "type", plugingetter.Acquire)
- func (p *Plugin) Release() {
- p.AddRefCount(plugingetter.Release)
- }
- // SetSpecOptModifier sets the function to use to modify the generated
- // runtime spec.
- func (p *Plugin) SetSpecOptModifier(f func(*specs.Spec)) {
- p.mu.Lock()
- p.modifyRuntimeSpec = f
- p.mu.Unlock()
- }
- // Timeout gets the currently configured connection timeout.
- // This should be used when dialing the plugin.
- func (p *Plugin) Timeout() time.Duration {
- p.mu.RLock()
- t := p.timeout
- p.mu.RUnlock()
- return t
- }
- // SetTimeout sets the timeout to use for dialing.
- func (p *Plugin) SetTimeout(t time.Duration) {
- p.mu.Lock()
- p.timeout = t
- p.mu.Unlock()
- }
- // Addr returns the net.Addr to use to connect to the plugin socket
- func (p *Plugin) Addr() net.Addr {
- p.mu.RLock()
- addr := p.addr
- p.mu.RUnlock()
- return addr
- }
- // SetAddr sets the plugin address which can be used for dialing the plugin.
- func (p *Plugin) SetAddr(addr net.Addr) {
- p.mu.Lock()
- p.addr = addr
- p.mu.Unlock()
- }
- // Protocol is the protocol that should be used for interacting with the plugin.
- func (p *Plugin) Protocol() string {
- if p.PluginObj.Config.Interface.ProtocolScheme != "" {
- return p.PluginObj.Config.Interface.ProtocolScheme
- }
- return plugins.ProtocolSchemeHTTPV1
- }
|