plugin.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. package v2
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "strings"
  9. "sync"
  10. "github.com/docker/docker/api/types"
  11. "github.com/docker/docker/oci"
  12. "github.com/docker/docker/pkg/plugingetter"
  13. "github.com/docker/docker/pkg/plugins"
  14. "github.com/docker/docker/pkg/system"
  15. specs "github.com/opencontainers/runtime-spec/specs-go"
  16. )
  17. // Plugin represents an individual plugin.
  18. type Plugin struct {
  19. mu sync.RWMutex
  20. PluginObj types.Plugin `json:"plugin"`
  21. pClient *plugins.Client
  22. runtimeSourcePath string
  23. refCount int
  24. LibRoot string // TODO: make private
  25. PropagatedMount string // TODO: make private
  26. Rootfs string // TODO: make private
  27. }
  28. const defaultPluginRuntimeDestination = "/run/docker/plugins"
  29. // ErrInadequateCapability indicates that the plugin did not have the requested capability.
  30. type ErrInadequateCapability struct {
  31. cap string
  32. }
  33. func (e ErrInadequateCapability) Error() string {
  34. return fmt.Sprintf("plugin does not provide %q capability", e.cap)
  35. }
  36. func newPluginObj(name, id, tag string) types.Plugin {
  37. return types.Plugin{Name: name, ID: id, Tag: tag}
  38. }
  39. // NewPlugin creates a plugin.
  40. func NewPlugin(name, id, runRoot, libRoot, tag string) *Plugin {
  41. return &Plugin{
  42. PluginObj: newPluginObj(name, id, tag),
  43. runtimeSourcePath: filepath.Join(runRoot, id),
  44. LibRoot: libRoot,
  45. }
  46. }
  47. // Restore restores the plugin
  48. func (p *Plugin) Restore(runRoot string) {
  49. p.runtimeSourcePath = filepath.Join(runRoot, p.GetID())
  50. }
  51. // GetRuntimeSourcePath gets the Source (host) path of the plugin socket
  52. // This path gets bind mounted into the plugin.
  53. func (p *Plugin) GetRuntimeSourcePath() string {
  54. p.mu.RLock()
  55. defer p.mu.RUnlock()
  56. return p.runtimeSourcePath
  57. }
  58. // BasePath returns the path to which all paths returned by the plugin are relative to.
  59. // For Plugin objects this returns the host path of the plugin container's rootfs.
  60. func (p *Plugin) BasePath() string {
  61. return p.Rootfs
  62. }
  63. // Client returns the plugin client.
  64. func (p *Plugin) Client() *plugins.Client {
  65. p.mu.RLock()
  66. defer p.mu.RUnlock()
  67. return p.pClient
  68. }
  69. // SetPClient set the plugin client.
  70. func (p *Plugin) SetPClient(client *plugins.Client) {
  71. p.mu.Lock()
  72. defer p.mu.Unlock()
  73. p.pClient = client
  74. }
  75. // IsV1 returns true for V1 plugins and false otherwise.
  76. func (p *Plugin) IsV1() bool {
  77. return false
  78. }
  79. // Name returns the plugin name.
  80. func (p *Plugin) Name() string {
  81. name := p.PluginObj.Name
  82. if len(p.PluginObj.Tag) > 0 {
  83. // TODO: this feels hacky, maybe we should be storing the distribution reference rather than splitting these
  84. name += ":" + p.PluginObj.Tag
  85. }
  86. return name
  87. }
  88. // FilterByCap query the plugin for a given capability.
  89. func (p *Plugin) FilterByCap(capability string) (*Plugin, error) {
  90. capability = strings.ToLower(capability)
  91. for _, typ := range p.PluginObj.Config.Interface.Types {
  92. if typ.Capability == capability && typ.Prefix == "docker" {
  93. return p, nil
  94. }
  95. }
  96. return nil, ErrInadequateCapability{capability}
  97. }
  98. // RemoveFromDisk deletes the plugin's runtime files from disk.
  99. func (p *Plugin) RemoveFromDisk() error {
  100. return os.RemoveAll(p.runtimeSourcePath)
  101. }
  102. // InitPlugin populates the plugin object from the plugin config file.
  103. func (p *Plugin) InitPlugin() error {
  104. dt, err := os.Open(filepath.Join(p.LibRoot, p.PluginObj.ID, "config.json"))
  105. if err != nil {
  106. return err
  107. }
  108. err = json.NewDecoder(dt).Decode(&p.PluginObj.Config)
  109. dt.Close()
  110. if err != nil {
  111. return err
  112. }
  113. p.PluginObj.Settings.Mounts = make([]types.PluginMount, len(p.PluginObj.Config.Mounts))
  114. copy(p.PluginObj.Settings.Mounts, p.PluginObj.Config.Mounts)
  115. p.PluginObj.Settings.Env = make([]string, 0, len(p.PluginObj.Config.Env))
  116. p.PluginObj.Settings.Devices = make([]types.PluginDevice, 0, len(p.PluginObj.Config.Linux.Devices))
  117. copy(p.PluginObj.Settings.Devices, p.PluginObj.Config.Linux.Devices)
  118. for _, env := range p.PluginObj.Config.Env {
  119. if env.Value != nil {
  120. p.PluginObj.Settings.Env = append(p.PluginObj.Settings.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
  121. }
  122. }
  123. p.PluginObj.Settings.Args = make([]string, len(p.PluginObj.Config.Args.Value))
  124. copy(p.PluginObj.Settings.Args, p.PluginObj.Config.Args.Value)
  125. return p.writeSettings()
  126. }
  127. func (p *Plugin) writeSettings() error {
  128. f, err := os.Create(filepath.Join(p.LibRoot, p.PluginObj.ID, "plugin-settings.json"))
  129. if err != nil {
  130. return err
  131. }
  132. err = json.NewEncoder(f).Encode(&p.PluginObj.Settings)
  133. f.Close()
  134. return err
  135. }
  136. // Set is used to pass arguments to the plugin.
  137. func (p *Plugin) Set(args []string) error {
  138. p.mu.Lock()
  139. defer p.mu.Unlock()
  140. if p.PluginObj.Enabled {
  141. return fmt.Errorf("cannot set on an active plugin, disable plugin before setting")
  142. }
  143. sets, err := newSettables(args)
  144. if err != nil {
  145. return err
  146. }
  147. // TODO(vieux): lots of code duplication here, needs to be refactored.
  148. next:
  149. for _, s := range sets {
  150. // range over all the envs in the config
  151. for _, env := range p.PluginObj.Config.Env {
  152. // found the env in the config
  153. if env.Name == s.name {
  154. // is it settable ?
  155. if ok, err := s.isSettable(allowedSettableFieldsEnv, env.Settable); err != nil {
  156. return err
  157. } else if !ok {
  158. return fmt.Errorf("%q is not settable", s.prettyName())
  159. }
  160. // is it, so lets update the settings in memory
  161. updateSettingsEnv(&p.PluginObj.Settings.Env, &s)
  162. continue next
  163. }
  164. }
  165. // range over all the mounts in the config
  166. for _, mount := range p.PluginObj.Config.Mounts {
  167. // found the mount in the config
  168. if mount.Name == s.name {
  169. // is it settable ?
  170. if ok, err := s.isSettable(allowedSettableFieldsMounts, mount.Settable); err != nil {
  171. return err
  172. } else if !ok {
  173. return fmt.Errorf("%q is not settable", s.prettyName())
  174. }
  175. // it is, so lets update the settings in memory
  176. *mount.Source = s.value
  177. continue next
  178. }
  179. }
  180. // range over all the devices in the config
  181. for _, device := range p.PluginObj.Config.Linux.Devices {
  182. // found the device in the config
  183. if device.Name == s.name {
  184. // is it settable ?
  185. if ok, err := s.isSettable(allowedSettableFieldsDevices, device.Settable); err != nil {
  186. return err
  187. } else if !ok {
  188. return fmt.Errorf("%q is not settable", s.prettyName())
  189. }
  190. // it is, so lets update the settings in memory
  191. *device.Path = s.value
  192. continue next
  193. }
  194. }
  195. // found the name in the config
  196. if p.PluginObj.Config.Args.Name == s.name {
  197. // is it settable ?
  198. if ok, err := s.isSettable(allowedSettableFieldsArgs, p.PluginObj.Config.Args.Settable); err != nil {
  199. return err
  200. } else if !ok {
  201. return fmt.Errorf("%q is not settable", s.prettyName())
  202. }
  203. // it is, so lets update the settings in memory
  204. p.PluginObj.Settings.Args = strings.Split(s.value, " ")
  205. continue next
  206. }
  207. return fmt.Errorf("setting %q not found in the plugin configuration", s.name)
  208. }
  209. // update the settings on disk
  210. return p.writeSettings()
  211. }
  212. // IsEnabled returns the active state of the plugin.
  213. func (p *Plugin) IsEnabled() bool {
  214. p.mu.RLock()
  215. defer p.mu.RUnlock()
  216. return p.PluginObj.Enabled
  217. }
  218. // GetID returns the plugin's ID.
  219. func (p *Plugin) GetID() string {
  220. p.mu.RLock()
  221. defer p.mu.RUnlock()
  222. return p.PluginObj.ID
  223. }
  224. // GetSocket returns the plugin socket.
  225. func (p *Plugin) GetSocket() string {
  226. p.mu.RLock()
  227. defer p.mu.RUnlock()
  228. return p.PluginObj.Config.Interface.Socket
  229. }
  230. // GetTypes returns the interface types of a plugin.
  231. func (p *Plugin) GetTypes() []types.PluginInterfaceType {
  232. p.mu.RLock()
  233. defer p.mu.RUnlock()
  234. return p.PluginObj.Config.Interface.Types
  235. }
  236. // GetRefCount returns the reference count.
  237. func (p *Plugin) GetRefCount() int {
  238. p.mu.RLock()
  239. defer p.mu.RUnlock()
  240. return p.refCount
  241. }
  242. // AddRefCount adds to reference count.
  243. func (p *Plugin) AddRefCount(count int) {
  244. p.mu.Lock()
  245. defer p.mu.Unlock()
  246. p.refCount += count
  247. }
  248. // Acquire increments the plugin's reference count
  249. // This should be followed up by `Release()` when the plugin is no longer in use.
  250. func (p *Plugin) Acquire() {
  251. p.AddRefCount(plugingetter.ACQUIRE)
  252. }
  253. // Release decrements the plugin's reference count
  254. // This should only be called when the plugin is no longer in use, e.g. with
  255. // via `Acquire()` or getter.Get("name", "type", plugingetter.ACQUIRE)
  256. func (p *Plugin) Release() {
  257. p.AddRefCount(plugingetter.RELEASE)
  258. }
  259. // InitSpec creates an OCI spec from the plugin's config.
  260. func (p *Plugin) InitSpec(s specs.Spec) (*specs.Spec, error) {
  261. s.Root = specs.Root{
  262. Path: p.Rootfs,
  263. Readonly: false, // TODO: all plugins should be readonly? settable in config?
  264. }
  265. userMounts := make(map[string]struct{}, len(p.PluginObj.Settings.Mounts))
  266. for _, m := range p.PluginObj.Settings.Mounts {
  267. userMounts[m.Destination] = struct{}{}
  268. }
  269. if err := os.MkdirAll(p.runtimeSourcePath, 0755); err != nil {
  270. return nil, err
  271. }
  272. mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
  273. Source: &p.runtimeSourcePath,
  274. Destination: defaultPluginRuntimeDestination,
  275. Type: "bind",
  276. Options: []string{"rbind", "rshared"},
  277. })
  278. if p.PluginObj.Config.Network.Type != "" {
  279. // TODO: if net == bridge, use libnetwork controller to create a new plugin-specific bridge, bind mount /etc/hosts and /etc/resolv.conf look at the docker code (allocateNetwork, initialize)
  280. if p.PluginObj.Config.Network.Type == "host" {
  281. oci.RemoveNamespace(&s, specs.NamespaceType("network"))
  282. }
  283. etcHosts := "/etc/hosts"
  284. resolvConf := "/etc/resolv.conf"
  285. mounts = append(mounts,
  286. types.PluginMount{
  287. Source: &etcHosts,
  288. Destination: etcHosts,
  289. Type: "bind",
  290. Options: []string{"rbind", "ro"},
  291. },
  292. types.PluginMount{
  293. Source: &resolvConf,
  294. Destination: resolvConf,
  295. Type: "bind",
  296. Options: []string{"rbind", "ro"},
  297. })
  298. }
  299. for _, mnt := range mounts {
  300. m := specs.Mount{
  301. Destination: mnt.Destination,
  302. Type: mnt.Type,
  303. Options: mnt.Options,
  304. }
  305. if mnt.Source == nil {
  306. return nil, errors.New("mount source is not specified")
  307. }
  308. m.Source = *mnt.Source
  309. s.Mounts = append(s.Mounts, m)
  310. }
  311. for i, m := range s.Mounts {
  312. if strings.HasPrefix(m.Destination, "/dev/") {
  313. if _, ok := userMounts[m.Destination]; ok {
  314. s.Mounts = append(s.Mounts[:i], s.Mounts[i+1:]...)
  315. }
  316. }
  317. }
  318. if p.PluginObj.Config.PropagatedMount != "" {
  319. p.PropagatedMount = filepath.Join(p.Rootfs, p.PluginObj.Config.PropagatedMount)
  320. s.Linux.RootfsPropagation = "rshared"
  321. }
  322. if p.PluginObj.Config.Linux.DeviceCreation {
  323. rwm := "rwm"
  324. s.Linux.Resources.Devices = []specs.DeviceCgroup{{Allow: true, Access: &rwm}}
  325. }
  326. for _, dev := range p.PluginObj.Settings.Devices {
  327. path := *dev.Path
  328. d, dPermissions, err := oci.DevicesFromPath(path, path, "rwm")
  329. if err != nil {
  330. return nil, err
  331. }
  332. s.Linux.Devices = append(s.Linux.Devices, d...)
  333. s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, dPermissions...)
  334. }
  335. envs := make([]string, 1, len(p.PluginObj.Settings.Env)+1)
  336. envs[0] = "PATH=" + system.DefaultPathEnv
  337. envs = append(envs, p.PluginObj.Settings.Env...)
  338. args := append(p.PluginObj.Config.Entrypoint, p.PluginObj.Settings.Args...)
  339. cwd := p.PluginObj.Config.Workdir
  340. if len(cwd) == 0 {
  341. cwd = "/"
  342. }
  343. s.Process.Terminal = false
  344. s.Process.Args = args
  345. s.Process.Cwd = cwd
  346. s.Process.Env = envs
  347. s.Process.Capabilities = append(s.Process.Capabilities, p.PluginObj.Config.Linux.Capabilities...)
  348. return &s, nil
  349. }