mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
f51a96c016
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
311 lines
8.3 KiB
Go
311 lines
8.3 KiB
Go
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"
|
|
"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
|
|
|
|
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 _, s := range sets {
|
|
// 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
|
|
}
|