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