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