plugin_linux.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. package v2 // import "github.com/docker/docker/plugin/v2"
  2. import (
  3. "os"
  4. "path/filepath"
  5. "runtime"
  6. "strings"
  7. "github.com/docker/docker/api/types"
  8. "github.com/docker/docker/oci"
  9. "github.com/docker/docker/pkg/system"
  10. specs "github.com/opencontainers/runtime-spec/specs-go"
  11. "github.com/pkg/errors"
  12. )
  13. // InitSpec creates an OCI spec from the plugin's config.
  14. func (p *Plugin) InitSpec(execRoot string) (*specs.Spec, error) {
  15. s := oci.DefaultSpec()
  16. s.Root = &specs.Root{
  17. Path: p.Rootfs,
  18. Readonly: false, // TODO: all plugins should be readonly? settable in config?
  19. }
  20. userMounts := make(map[string]struct{}, len(p.PluginObj.Settings.Mounts))
  21. for _, m := range p.PluginObj.Settings.Mounts {
  22. userMounts[m.Destination] = struct{}{}
  23. }
  24. execRoot = filepath.Join(execRoot, p.PluginObj.ID)
  25. if err := os.MkdirAll(execRoot, 0700); err != nil {
  26. return nil, errors.WithStack(err)
  27. }
  28. if p.PluginObj.Config.PropagatedMount != "" {
  29. pRoot := filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount")
  30. s.Mounts = append(s.Mounts, specs.Mount{
  31. Source: pRoot,
  32. Destination: p.PluginObj.Config.PropagatedMount,
  33. Type: "bind",
  34. Options: []string{"rbind", "rw", "rshared"},
  35. })
  36. s.Linux.RootfsPropagation = "rshared"
  37. }
  38. mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
  39. Source: &execRoot,
  40. Destination: defaultPluginRuntimeDestination,
  41. Type: "bind",
  42. Options: []string{"rbind", "rshared"},
  43. })
  44. if p.PluginObj.Config.Network.Type != "" {
  45. // 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)
  46. if p.PluginObj.Config.Network.Type == "host" {
  47. oci.RemoveNamespace(&s, specs.LinuxNamespaceType("network"))
  48. }
  49. etcHosts := "/etc/hosts"
  50. resolvConf := "/etc/resolv.conf"
  51. mounts = append(mounts,
  52. types.PluginMount{
  53. Source: &etcHosts,
  54. Destination: etcHosts,
  55. Type: "bind",
  56. Options: []string{"rbind", "ro"},
  57. },
  58. types.PluginMount{
  59. Source: &resolvConf,
  60. Destination: resolvConf,
  61. Type: "bind",
  62. Options: []string{"rbind", "ro"},
  63. })
  64. }
  65. if p.PluginObj.Config.PidHost {
  66. oci.RemoveNamespace(&s, specs.LinuxNamespaceType("pid"))
  67. }
  68. if p.PluginObj.Config.IpcHost {
  69. oci.RemoveNamespace(&s, specs.LinuxNamespaceType("ipc"))
  70. }
  71. for _, mnt := range mounts {
  72. m := specs.Mount{
  73. Destination: mnt.Destination,
  74. Type: mnt.Type,
  75. Options: mnt.Options,
  76. }
  77. if mnt.Source == nil {
  78. return nil, errors.New("mount source is not specified")
  79. }
  80. m.Source = *mnt.Source
  81. s.Mounts = append(s.Mounts, m)
  82. }
  83. for i, m := range s.Mounts {
  84. if strings.HasPrefix(m.Destination, "/dev/") {
  85. if _, ok := userMounts[m.Destination]; ok {
  86. s.Mounts = append(s.Mounts[:i], s.Mounts[i+1:]...)
  87. }
  88. }
  89. }
  90. if p.PluginObj.Config.Linux.AllowAllDevices {
  91. s.Linux.Resources.Devices = []specs.LinuxDeviceCgroup{{Allow: true, Access: "rwm"}}
  92. }
  93. for _, dev := range p.PluginObj.Settings.Devices {
  94. path := *dev.Path
  95. d, dPermissions, err := oci.DevicesFromPath(path, path, "rwm")
  96. if err != nil {
  97. return nil, errors.WithStack(err)
  98. }
  99. s.Linux.Devices = append(s.Linux.Devices, d...)
  100. s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, dPermissions...)
  101. }
  102. envs := make([]string, 1, len(p.PluginObj.Settings.Env)+1)
  103. envs[0] = "PATH=" + system.DefaultPathEnv(runtime.GOOS)
  104. envs = append(envs, p.PluginObj.Settings.Env...)
  105. args := append(p.PluginObj.Config.Entrypoint, p.PluginObj.Settings.Args...)
  106. cwd := p.PluginObj.Config.WorkDir
  107. if len(cwd) == 0 {
  108. cwd = "/"
  109. }
  110. s.Process.Terminal = false
  111. s.Process.Args = args
  112. s.Process.Cwd = cwd
  113. s.Process.Env = envs
  114. caps := s.Process.Capabilities
  115. caps.Bounding = append(caps.Bounding, p.PluginObj.Config.Linux.Capabilities...)
  116. caps.Permitted = append(caps.Permitted, p.PluginObj.Config.Linux.Capabilities...)
  117. caps.Inheritable = append(caps.Inheritable, p.PluginObj.Config.Linux.Capabilities...)
  118. caps.Effective = append(caps.Effective, p.PluginObj.Config.Linux.Capabilities...)
  119. if p.modifyRuntimeSpec != nil {
  120. p.modifyRuntimeSpec(&s)
  121. }
  122. return &s, nil
  123. }