1package v2 // import "github.com/docker/docker/plugin/v2" 2 3import ( 4 "fmt" 5 "net" 6 "path/filepath" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/docker/docker/api/types" 12 "github.com/docker/docker/pkg/plugingetter" 13 "github.com/docker/docker/pkg/plugins" 14 "github.com/opencontainers/go-digest" 15 "github.com/opencontainers/runtime-spec/specs-go" 16) 17 18// Plugin represents an individual plugin. 19type Plugin struct { 20 mu sync.RWMutex 21 PluginObj types.Plugin `json:"plugin"` // todo: embed struct 22 pClient *plugins.Client 23 refCount int 24 Rootfs string // TODO: make private 25 26 Config digest.Digest 27 Blobsums []digest.Digest 28 29 modifyRuntimeSpec func(*specs.Spec) 30 31 SwarmServiceID string 32 timeout time.Duration 33 addr net.Addr 34} 35 36const defaultPluginRuntimeDestination = "/run/docker/plugins" 37 38// ErrInadequateCapability indicates that the plugin did not have the requested capability. 39type ErrInadequateCapability struct { 40 cap string 41} 42 43func (e ErrInadequateCapability) Error() string { 44 return fmt.Sprintf("plugin does not provide %q capability", e.cap) 45} 46 47// ScopedPath returns the path scoped to the plugin rootfs 48func (p *Plugin) ScopedPath(s string) string { 49 if p.PluginObj.Config.PropagatedMount != "" && strings.HasPrefix(s, p.PluginObj.Config.PropagatedMount) { 50 // re-scope to the propagated mount path on the host 51 return filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount", strings.TrimPrefix(s, p.PluginObj.Config.PropagatedMount)) 52 } 53 return filepath.Join(p.Rootfs, s) 54} 55 56// Client returns the plugin client. 57// Deprecated: use p.Addr() and manually create the client 58func (p *Plugin) Client() *plugins.Client { 59 p.mu.RLock() 60 defer p.mu.RUnlock() 61 62 return p.pClient 63} 64 65// SetPClient set the plugin client. 66// Deprecated: Hardcoded plugin client is deprecated 67func (p *Plugin) SetPClient(client *plugins.Client) { 68 p.mu.Lock() 69 defer p.mu.Unlock() 70 71 p.pClient = client 72} 73 74// IsV1 returns true for V1 plugins and false otherwise. 75func (p *Plugin) IsV1() bool { 76 return false 77} 78 79// Name returns the plugin name. 80func (p *Plugin) Name() string { 81 return p.PluginObj.Name 82} 83 84// FilterByCap query the plugin for a given capability. 85func (p *Plugin) FilterByCap(capability string) (*Plugin, error) { 86 capability = strings.ToLower(capability) 87 for _, typ := range p.PluginObj.Config.Interface.Types { 88 if typ.Capability == capability && typ.Prefix == "docker" { 89 return p, nil 90 } 91 } 92 return nil, ErrInadequateCapability{capability} 93} 94 95// InitEmptySettings initializes empty settings for a plugin. 96func (p *Plugin) InitEmptySettings() { 97 p.PluginObj.Settings.Mounts = make([]types.PluginMount, len(p.PluginObj.Config.Mounts)) 98 copy(p.PluginObj.Settings.Mounts, p.PluginObj.Config.Mounts) 99 p.PluginObj.Settings.Devices = make([]types.PluginDevice, len(p.PluginObj.Config.Linux.Devices)) 100 copy(p.PluginObj.Settings.Devices, p.PluginObj.Config.Linux.Devices) 101 p.PluginObj.Settings.Env = make([]string, 0, len(p.PluginObj.Config.Env)) 102 for _, env := range p.PluginObj.Config.Env { 103 if env.Value != nil { 104 p.PluginObj.Settings.Env = append(p.PluginObj.Settings.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value)) 105 } 106 } 107 p.PluginObj.Settings.Args = make([]string, len(p.PluginObj.Config.Args.Value)) 108 copy(p.PluginObj.Settings.Args, p.PluginObj.Config.Args.Value) 109} 110 111// Set is used to pass arguments to the plugin. 112func (p *Plugin) Set(args []string) error { 113 p.mu.Lock() 114 defer p.mu.Unlock() 115 116 if p.PluginObj.Enabled { 117 return fmt.Errorf("cannot set on an active plugin, disable plugin before setting") 118 } 119 120 sets, err := newSettables(args) 121 if err != nil { 122 return err 123 } 124 125 // TODO(vieux): lots of code duplication here, needs to be refactored. 126 127next: 128 for _, s := range sets { 129 // range over all the envs in the config 130 for _, env := range p.PluginObj.Config.Env { 131 // found the env in the config 132 if env.Name == s.name { 133 // is it settable ? 134 if ok, err := s.isSettable(allowedSettableFieldsEnv, env.Settable); err != nil { 135 return err 136 } else if !ok { 137 return fmt.Errorf("%q is not settable", s.prettyName()) 138 } 139 // is it, so lets update the settings in memory 140 updateSettingsEnv(&p.PluginObj.Settings.Env, &s) 141 continue next 142 } 143 } 144 145 // range over all the mounts in the config 146 for _, mount := range p.PluginObj.Config.Mounts { 147 // found the mount in the config 148 if mount.Name == s.name { 149 // is it settable ? 150 if ok, err := s.isSettable(allowedSettableFieldsMounts, mount.Settable); err != nil { 151 return err 152 } else if !ok { 153 return fmt.Errorf("%q is not settable", s.prettyName()) 154 } 155 156 // it is, so lets update the settings in memory 157 if mount.Source == nil { 158 return fmt.Errorf("Plugin config has no mount source") 159 } 160 *mount.Source = s.value 161 continue next 162 } 163 } 164 165 // range over all the devices in the config 166 for _, device := range p.PluginObj.Config.Linux.Devices { 167 // found the device in the config 168 if device.Name == s.name { 169 // is it settable ? 170 if ok, err := s.isSettable(allowedSettableFieldsDevices, device.Settable); err != nil { 171 return err 172 } else if !ok { 173 return fmt.Errorf("%q is not settable", s.prettyName()) 174 } 175 176 // it is, so lets update the settings in memory 177 if device.Path == nil { 178 return fmt.Errorf("Plugin config has no device path") 179 } 180 *device.Path = s.value 181 continue next 182 } 183 } 184 185 // found the name in the config 186 if p.PluginObj.Config.Args.Name == s.name { 187 // is it settable ? 188 if ok, err := s.isSettable(allowedSettableFieldsArgs, p.PluginObj.Config.Args.Settable); err != nil { 189 return err 190 } else if !ok { 191 return fmt.Errorf("%q is not settable", s.prettyName()) 192 } 193 194 // it is, so lets update the settings in memory 195 p.PluginObj.Settings.Args = strings.Split(s.value, " ") 196 continue next 197 } 198 199 return fmt.Errorf("setting %q not found in the plugin configuration", s.name) 200 } 201 202 return nil 203} 204 205// IsEnabled returns the active state of the plugin. 206func (p *Plugin) IsEnabled() bool { 207 p.mu.RLock() 208 defer p.mu.RUnlock() 209 210 return p.PluginObj.Enabled 211} 212 213// GetID returns the plugin's ID. 214func (p *Plugin) GetID() string { 215 p.mu.RLock() 216 defer p.mu.RUnlock() 217 218 return p.PluginObj.ID 219} 220 221// GetSocket returns the plugin socket. 222func (p *Plugin) GetSocket() string { 223 p.mu.RLock() 224 defer p.mu.RUnlock() 225 226 return p.PluginObj.Config.Interface.Socket 227} 228 229// GetTypes returns the interface types of a plugin. 230func (p *Plugin) GetTypes() []types.PluginInterfaceType { 231 p.mu.RLock() 232 defer p.mu.RUnlock() 233 234 return p.PluginObj.Config.Interface.Types 235} 236 237// GetRefCount returns the reference count. 238func (p *Plugin) GetRefCount() int { 239 p.mu.RLock() 240 defer p.mu.RUnlock() 241 242 return p.refCount 243} 244 245// AddRefCount adds to reference count. 246func (p *Plugin) AddRefCount(count int) { 247 p.mu.Lock() 248 defer p.mu.Unlock() 249 250 p.refCount += count 251} 252 253// Acquire increments the plugin's reference count 254// This should be followed up by `Release()` when the plugin is no longer in use. 255func (p *Plugin) Acquire() { 256 p.AddRefCount(plugingetter.Acquire) 257} 258 259// Release decrements the plugin's reference count 260// This should only be called when the plugin is no longer in use, e.g. with 261// via `Acquire()` or getter.Get("name", "type", plugingetter.Acquire) 262func (p *Plugin) Release() { 263 p.AddRefCount(plugingetter.Release) 264} 265 266// SetSpecOptModifier sets the function to use to modify the generated 267// runtime spec. 268func (p *Plugin) SetSpecOptModifier(f func(*specs.Spec)) { 269 p.mu.Lock() 270 p.modifyRuntimeSpec = f 271 p.mu.Unlock() 272} 273 274// Timeout gets the currently configured connection timeout. 275// This should be used when dialing the plugin. 276func (p *Plugin) Timeout() time.Duration { 277 p.mu.RLock() 278 t := p.timeout 279 p.mu.RUnlock() 280 return t 281} 282 283// SetTimeout sets the timeout to use for dialing. 284func (p *Plugin) SetTimeout(t time.Duration) { 285 p.mu.Lock() 286 p.timeout = t 287 p.mu.Unlock() 288} 289 290// Addr returns the net.Addr to use to connect to the plugin socket 291func (p *Plugin) Addr() net.Addr { 292 p.mu.RLock() 293 addr := p.addr 294 p.mu.RUnlock() 295 return addr 296} 297 298// SetAddr sets the plugin address which can be used for dialing the plugin. 299func (p *Plugin) SetAddr(addr net.Addr) { 300 p.mu.Lock() 301 p.addr = addr 302 p.mu.Unlock() 303} 304 305// Protocol is the protocol that should be used for interacting with the plugin. 306func (p *Plugin) Protocol() string { 307 if p.PluginObj.Config.Interface.ProtocolScheme != "" { 308 return p.PluginObj.Config.Interface.ProtocolScheme 309 } 310 return plugins.ProtocolSchemeHTTPV1 311} 312