Split plugin package into `store` and `v2/plugin`. Now the functionality
is clearly delineated:
- Manager: Manages the global state of the plugin sub-system.
- PluginStore: Manages a collection of plugins (in memory and on-disk)
- Plugin: Manages the single plugin unit.
This also facilitates splitting the global PluginManager lock into:
- PluginManager lock to protect global states.
- PluginStore lock to protect store states.
- Plugin lock to protect individual plugin states.
Importing "github.com/docker/docker/plugin/store" will provide access
to plugins and has lesser dependencies when compared to importing the
original monolithic `plugin package`.
Signed-off-by: Anusha Ragunathan <anusha@docker.com>
| ... | ... |
@@ -15,39 +15,40 @@ import ( |
| 15 | 15 |
"github.com/docker/docker/pkg/archive" |
| 16 | 16 |
"github.com/docker/docker/pkg/stringid" |
| 17 | 17 |
"github.com/docker/docker/plugin/distribution" |
| 18 |
+ "github.com/docker/docker/plugin/v2" |
|
| 18 | 19 |
"github.com/docker/docker/reference" |
| 19 | 20 |
"github.com/docker/engine-api/types" |
| 20 | 21 |
) |
| 21 | 22 |
|
| 22 | 23 |
// Disable deactivates a plugin, which implies that they cannot be used by containers. |
| 23 | 24 |
func (pm *Manager) Disable(name string) error {
|
| 24 |
- p, err := pm.get(name) |
|
| 25 |
+ p, err := pm.pluginStore.GetByName(name) |
|
| 25 | 26 |
if err != nil {
|
| 26 | 27 |
return err |
| 27 | 28 |
} |
| 28 | 29 |
if err := pm.disable(p); err != nil {
|
| 29 | 30 |
return err |
| 30 | 31 |
} |
| 31 |
- pm.pluginEventLogger(p.PluginObj.ID, name, "disable") |
|
| 32 |
+ pm.pluginEventLogger(p.GetID(), name, "disable") |
|
| 32 | 33 |
return nil |
| 33 | 34 |
} |
| 34 | 35 |
|
| 35 | 36 |
// Enable activates a plugin, which implies that they are ready to be used by containers. |
| 36 | 37 |
func (pm *Manager) Enable(name string) error {
|
| 37 |
- p, err := pm.get(name) |
|
| 38 |
+ p, err := pm.pluginStore.GetByName(name) |
|
| 38 | 39 |
if err != nil {
|
| 39 | 40 |
return err |
| 40 | 41 |
} |
| 41 | 42 |
if err := pm.enable(p, false); err != nil {
|
| 42 | 43 |
return err |
| 43 | 44 |
} |
| 44 |
- pm.pluginEventLogger(p.PluginObj.ID, name, "enable") |
|
| 45 |
+ pm.pluginEventLogger(p.GetID(), name, "enable") |
|
| 45 | 46 |
return nil |
| 46 | 47 |
} |
| 47 | 48 |
|
| 48 | 49 |
// Inspect examines a plugin manifest |
| 49 | 50 |
func (pm *Manager) Inspect(name string) (tp types.Plugin, err error) {
|
| 50 |
- p, err := pm.get(name) |
|
| 51 |
+ p, err := pm.pluginStore.GetByName(name) |
|
| 51 | 52 |
if err != nil {
|
| 52 | 53 |
return tp, err |
| 53 | 54 |
} |
| ... | ... |
@@ -63,7 +64,7 @@ func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.A |
| 63 | 63 |
} |
| 64 | 64 |
name = ref.String() |
| 65 | 65 |
|
| 66 |
- if p, _ := pm.get(name); p != nil {
|
|
| 66 |
+ if p, _ := pm.pluginStore.GetByName(name); p != nil {
|
|
| 67 | 67 |
logrus.Debugf("plugin already exists")
|
| 68 | 68 |
return nil, fmt.Errorf("%s exists", name)
|
| 69 | 69 |
} |
| ... | ... |
@@ -86,25 +87,25 @@ func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.A |
| 86 | 86 |
return nil, err |
| 87 | 87 |
} |
| 88 | 88 |
|
| 89 |
- p := pm.newPlugin(ref, pluginID) |
|
| 90 |
- if err := pm.initPlugin(p); err != nil {
|
|
| 89 |
+ var tag string |
|
| 90 |
+ if ref, ok := ref.(reference.NamedTagged); ok {
|
|
| 91 |
+ tag = ref.Tag() |
|
| 92 |
+ } |
|
| 93 |
+ p := v2.NewPlugin(ref.Name(), pluginID, pm.runRoot, tag) |
|
| 94 |
+ if err := p.InitPlugin(pm.libRoot); err != nil {
|
|
| 91 | 95 |
return nil, err |
| 92 | 96 |
} |
| 93 |
- |
|
| 94 |
- pm.Lock() |
|
| 95 |
- pm.plugins[pluginID] = p |
|
| 96 |
- pm.nameToID[name] = pluginID |
|
| 97 |
- pm.save() |
|
| 98 |
- pm.Unlock() |
|
| 97 |
+ pm.pluginStore.Add(p) |
|
| 99 | 98 |
|
| 100 | 99 |
pm.pluginEventLogger(pluginID, name, "pull") |
| 101 |
- return computePrivileges(&p.PluginObj.Manifest), nil |
|
| 100 |
+ return p.ComputePrivileges(), nil |
|
| 102 | 101 |
} |
| 103 | 102 |
|
| 104 | 103 |
// List displays the list of plugins and associated metadata. |
| 105 | 104 |
func (pm *Manager) List() ([]types.Plugin, error) {
|
| 106 |
- out := make([]types.Plugin, 0, len(pm.plugins)) |
|
| 107 |
- for _, p := range pm.plugins {
|
|
| 105 |
+ plugins := pm.pluginStore.GetAll() |
|
| 106 |
+ out := make([]types.Plugin, 0, len(plugins)) |
|
| 107 |
+ for _, p := range plugins {
|
|
| 108 | 108 |
out = append(out, p.PluginObj) |
| 109 | 109 |
} |
| 110 | 110 |
return out, nil |
| ... | ... |
@@ -112,11 +113,11 @@ func (pm *Manager) List() ([]types.Plugin, error) {
|
| 112 | 112 |
|
| 113 | 113 |
// Push pushes a plugin to the store. |
| 114 | 114 |
func (pm *Manager) Push(name string, metaHeader http.Header, authConfig *types.AuthConfig) error {
|
| 115 |
- p, err := pm.get(name) |
|
| 115 |
+ p, err := pm.pluginStore.GetByName(name) |
|
| 116 | 116 |
if err != nil {
|
| 117 | 117 |
return err |
| 118 | 118 |
} |
| 119 |
- dest := filepath.Join(pm.libRoot, p.PluginObj.ID) |
|
| 119 |
+ dest := filepath.Join(pm.libRoot, p.GetID()) |
|
| 120 | 120 |
config, err := ioutil.ReadFile(filepath.Join(dest, "manifest.json")) |
| 121 | 121 |
if err != nil {
|
| 122 | 122 |
return err |
| ... | ... |
@@ -142,22 +143,28 @@ func (pm *Manager) Push(name string, metaHeader http.Header, authConfig *types.A |
| 142 | 142 |
|
| 143 | 143 |
// Remove deletes plugin's root directory. |
| 144 | 144 |
func (pm *Manager) Remove(name string, config *types.PluginRmConfig) error {
|
| 145 |
- p, err := pm.get(name) |
|
| 145 |
+ p, err := pm.pluginStore.GetByName(name) |
|
| 146 | 146 |
if err != nil {
|
| 147 | 147 |
return err |
| 148 | 148 |
} |
| 149 |
- if err := pm.remove(p, config.ForceRemove); err != nil {
|
|
| 150 |
- return err |
|
| 149 |
+ if p.IsEnabled() {
|
|
| 150 |
+ if !config.ForceRemove {
|
|
| 151 |
+ return fmt.Errorf("plugin %s is enabled", p.Name())
|
|
| 152 |
+ } |
|
| 153 |
+ if err := pm.disable(p); err != nil {
|
|
| 154 |
+ logrus.Errorf("failed to disable plugin '%s': %s", p.Name(), err)
|
|
| 155 |
+ } |
|
| 151 | 156 |
} |
| 152 |
- pm.pluginEventLogger(p.PluginObj.ID, name, "remove") |
|
| 157 |
+ pm.pluginStore.Remove(p) |
|
| 158 |
+ pm.pluginEventLogger(p.GetID(), name, "remove") |
|
| 153 | 159 |
return nil |
| 154 | 160 |
} |
| 155 | 161 |
|
| 156 | 162 |
// Set sets plugin args |
| 157 | 163 |
func (pm *Manager) Set(name string, args []string) error {
|
| 158 |
- p, err := pm.get(name) |
|
| 164 |
+ p, err := pm.pluginStore.GetByName(name) |
|
| 159 | 165 |
if err != nil {
|
| 160 | 166 |
return err |
| 161 | 167 |
} |
| 162 |
- return pm.set(p, args) |
|
| 168 |
+ return p.Set(args) |
|
| 163 | 169 |
} |
| 164 | 170 |
deleted file mode 100644 |
| ... | ... |
@@ -1,10 +0,0 @@ |
| 1 |
-package plugin |
|
| 2 |
- |
|
| 3 |
-import "github.com/docker/docker/pkg/plugins" |
|
| 4 |
- |
|
| 5 |
-// Plugin represents a plugin. It is used to abstract from an older plugin architecture (in pkg/plugins). |
|
| 6 |
-type Plugin interface {
|
|
| 7 |
- Client() *plugins.Client |
|
| 8 |
- Name() string |
|
| 9 |
- IsLegacy() bool |
|
| 10 |
-} |
| 11 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,23 +0,0 @@ |
| 1 |
-// +build !experimental |
|
| 2 |
- |
|
| 3 |
-package plugin |
|
| 4 |
- |
|
| 5 |
-import "github.com/docker/docker/pkg/plugins" |
|
| 6 |
- |
|
| 7 |
-// FindWithCapability returns a list of plugins matching the given capability. |
|
| 8 |
-func FindWithCapability(capability string) ([]Plugin, error) {
|
|
| 9 |
- pl, err := plugins.GetAll(capability) |
|
| 10 |
- if err != nil {
|
|
| 11 |
- return nil, err |
|
| 12 |
- } |
|
| 13 |
- result := make([]Plugin, len(pl)) |
|
| 14 |
- for i, p := range pl {
|
|
| 15 |
- result[i] = p |
|
| 16 |
- } |
|
| 17 |
- return result, nil |
|
| 18 |
-} |
|
| 19 |
- |
|
| 20 |
-// LookupWithCapability returns a plugin matching the given name and capability. |
|
| 21 |
-func LookupWithCapability(name, capability string) (Plugin, error) {
|
|
| 22 |
- return plugins.Get(name, capability) |
|
| 23 |
-} |
| ... | ... |
@@ -4,7 +4,6 @@ package plugin |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 | 6 |
"encoding/json" |
| 7 |
- "errors" |
|
| 8 | 7 |
"fmt" |
| 9 | 8 |
"io" |
| 10 | 9 |
"os" |
| ... | ... |
@@ -14,16 +13,12 @@ import ( |
| 14 | 14 |
|
| 15 | 15 |
"github.com/Sirupsen/logrus" |
| 16 | 16 |
"github.com/docker/docker/libcontainerd" |
| 17 |
- "github.com/docker/docker/pkg/ioutils" |
|
| 18 | 17 |
"github.com/docker/docker/pkg/plugins" |
| 19 |
- "github.com/docker/docker/reference" |
|
| 18 |
+ "github.com/docker/docker/plugin/store" |
|
| 19 |
+ "github.com/docker/docker/plugin/v2" |
|
| 20 | 20 |
"github.com/docker/docker/registry" |
| 21 |
- "github.com/docker/docker/restartmanager" |
|
| 22 |
- "github.com/docker/engine-api/types" |
|
| 23 | 21 |
) |
| 24 | 22 |
|
| 25 |
-const defaultPluginRuntimeDestination = "/run/docker/plugins" |
|
| 26 |
- |
|
| 27 | 23 |
var ( |
| 28 | 24 |
manager *Manager |
| 29 | 25 |
|
| ... | ... |
@@ -34,71 +29,14 @@ var ( |
| 34 | 34 |
allowV1PluginsFallback = true |
| 35 | 35 |
) |
| 36 | 36 |
|
| 37 |
-// ErrNotFound indicates that a plugin was not found locally. |
|
| 38 |
-type ErrNotFound string |
|
| 39 |
- |
|
| 40 |
-func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }
|
|
| 41 |
- |
|
| 42 |
-// ErrInadequateCapability indicates that a plugin was found but did not have the requested capability. |
|
| 43 |
-type ErrInadequateCapability struct {
|
|
| 44 |
- name string |
|
| 45 |
- capability string |
|
| 46 |
-} |
|
| 47 |
- |
|
| 48 |
-func (e ErrInadequateCapability) Error() string {
|
|
| 49 |
- return fmt.Sprintf("plugin %q found, but not with %q capability", e.name, e.capability)
|
|
| 50 |
-} |
|
| 51 |
- |
|
| 52 |
-type plugin struct {
|
|
| 53 |
- //sync.RWMutex TODO |
|
| 54 |
- PluginObj types.Plugin `json:"plugin"` |
|
| 55 |
- client *plugins.Client |
|
| 56 |
- restartManager restartmanager.RestartManager |
|
| 57 |
- runtimeSourcePath string |
|
| 58 |
- exitChan chan bool |
|
| 59 |
-} |
|
| 60 |
- |
|
| 61 |
-func (p *plugin) Client() *plugins.Client {
|
|
| 62 |
- return p.client |
|
| 63 |
-} |
|
| 64 |
- |
|
| 65 |
-// IsLegacy returns true for legacy plugins and false otherwise. |
|
| 66 |
-func (p *plugin) IsLegacy() bool {
|
|
| 67 |
- return false |
|
| 68 |
-} |
|
| 69 |
- |
|
| 70 |
-func (p *plugin) Name() string {
|
|
| 71 |
- name := p.PluginObj.Name |
|
| 72 |
- if len(p.PluginObj.Tag) > 0 {
|
|
| 73 |
- // TODO: this feels hacky, maybe we should be storing the distribution reference rather than splitting these |
|
| 74 |
- name += ":" + p.PluginObj.Tag |
|
| 75 |
- } |
|
| 76 |
- return name |
|
| 77 |
-} |
|
| 78 |
- |
|
| 79 |
-func (pm *Manager) newPlugin(ref reference.Named, id string) *plugin {
|
|
| 80 |
- p := &plugin{
|
|
| 81 |
- PluginObj: types.Plugin{
|
|
| 82 |
- Name: ref.Name(), |
|
| 83 |
- ID: id, |
|
| 84 |
- }, |
|
| 85 |
- runtimeSourcePath: filepath.Join(pm.runRoot, id), |
|
| 86 |
- } |
|
| 87 |
- if ref, ok := ref.(reference.NamedTagged); ok {
|
|
| 88 |
- p.PluginObj.Tag = ref.Tag() |
|
| 89 |
- } |
|
| 90 |
- return p |
|
| 91 |
-} |
|
| 92 |
- |
|
| 93 |
-func (pm *Manager) restorePlugin(p *plugin) error {
|
|
| 94 |
- p.runtimeSourcePath = filepath.Join(pm.runRoot, p.PluginObj.ID) |
|
| 95 |
- if p.PluginObj.Enabled {
|
|
| 37 |
+func (pm *Manager) restorePlugin(p *v2.Plugin) error {
|
|
| 38 |
+ p.RuntimeSourcePath = filepath.Join(pm.runRoot, p.GetID()) |
|
| 39 |
+ if p.IsEnabled() {
|
|
| 96 | 40 |
return pm.restore(p) |
| 97 | 41 |
} |
| 98 | 42 |
return nil |
| 99 | 43 |
} |
| 100 | 44 |
|
| 101 |
-type pluginMap map[string]*plugin |
|
| 102 | 45 |
type eventLogger func(id, name, action string) |
| 103 | 46 |
|
| 104 | 47 |
// Manager controls the plugin subsystem. |
| ... | ... |
@@ -106,8 +44,7 @@ type Manager struct {
|
| 106 | 106 |
sync.RWMutex |
| 107 | 107 |
libRoot string |
| 108 | 108 |
runRoot string |
| 109 |
- plugins pluginMap // TODO: figure out why save() doesn't json encode *plugin object |
|
| 110 |
- nameToID map[string]string |
|
| 109 |
+ pluginStore *store.PluginStore |
|
| 111 | 110 |
handlers map[string]func(string, *plugins.Client) |
| 112 | 111 |
containerdClient libcontainerd.Client |
| 113 | 112 |
registryService registry.Service |
| ... | ... |
@@ -132,8 +69,7 @@ func Init(root string, remote libcontainerd.Remote, rs registry.Service, liveRes |
| 132 | 132 |
manager = &Manager{
|
| 133 | 133 |
libRoot: root, |
| 134 | 134 |
runRoot: "/run/docker", |
| 135 |
- plugins: make(map[string]*plugin), |
|
| 136 |
- nameToID: make(map[string]string), |
|
| 135 |
+ pluginStore: store.NewPluginStore(root), |
|
| 137 | 136 |
handlers: make(map[string]func(string, *plugins.Client)), |
| 138 | 137 |
registryService: rs, |
| 139 | 138 |
liveRestore: liveRestore, |
| ... | ... |
@@ -162,105 +98,6 @@ func Handle(capability string, callback func(string, *plugins.Client)) {
|
| 162 | 162 |
} |
| 163 | 163 |
} |
| 164 | 164 |
|
| 165 |
-func (pm *Manager) get(name string) (*plugin, error) {
|
|
| 166 |
- pm.RLock() |
|
| 167 |
- defer pm.RUnlock() |
|
| 168 |
- |
|
| 169 |
- id, nameOk := pm.nameToID[name] |
|
| 170 |
- if !nameOk {
|
|
| 171 |
- return nil, ErrNotFound(name) |
|
| 172 |
- } |
|
| 173 |
- |
|
| 174 |
- p, idOk := pm.plugins[id] |
|
| 175 |
- if !idOk {
|
|
| 176 |
- return nil, ErrNotFound(name) |
|
| 177 |
- } |
|
| 178 |
- |
|
| 179 |
- return p, nil |
|
| 180 |
-} |
|
| 181 |
- |
|
| 182 |
-// FindWithCapability returns a list of plugins matching the given capability. |
|
| 183 |
-func FindWithCapability(capability string) ([]Plugin, error) {
|
|
| 184 |
- result := make([]Plugin, 0, 1) |
|
| 185 |
- |
|
| 186 |
- /* Daemon start always calls plugin.Init thereby initializing a manager. |
|
| 187 |
- * So manager on experimental builds can never be nil, even while |
|
| 188 |
- * handling legacy plugins. However, there are legacy plugin unit |
|
| 189 |
- * tests where volume subsystem directly talks with the plugin, |
|
| 190 |
- * bypassing the daemon. For such tests, this check is necessary.*/ |
|
| 191 |
- if manager != nil {
|
|
| 192 |
- manager.RLock() |
|
| 193 |
- for _, p := range manager.plugins {
|
|
| 194 |
- for _, typ := range p.PluginObj.Manifest.Interface.Types {
|
|
| 195 |
- if strings.EqualFold(typ.Capability, capability) && typ.Prefix == "docker" {
|
|
| 196 |
- result = append(result, p) |
|
| 197 |
- break |
|
| 198 |
- } |
|
| 199 |
- } |
|
| 200 |
- } |
|
| 201 |
- manager.RUnlock() |
|
| 202 |
- } |
|
| 203 |
- |
|
| 204 |
- // Lookup with legacy model. |
|
| 205 |
- if allowV1PluginsFallback {
|
|
| 206 |
- pl, err := plugins.GetAll(capability) |
|
| 207 |
- if err != nil {
|
|
| 208 |
- return nil, fmt.Errorf("legacy plugin: %v", err)
|
|
| 209 |
- } |
|
| 210 |
- for _, p := range pl {
|
|
| 211 |
- result = append(result, p) |
|
| 212 |
- } |
|
| 213 |
- } |
|
| 214 |
- return result, nil |
|
| 215 |
-} |
|
| 216 |
- |
|
| 217 |
-// LookupWithCapability returns a plugin matching the given name and capability. |
|
| 218 |
-func LookupWithCapability(name, capability string) (Plugin, error) {
|
|
| 219 |
- var ( |
|
| 220 |
- p *plugin |
|
| 221 |
- err error |
|
| 222 |
- ) |
|
| 223 |
- |
|
| 224 |
- // Lookup using new model. |
|
| 225 |
- if manager != nil {
|
|
| 226 |
- fullName := name |
|
| 227 |
- if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
|
|
| 228 |
- if reference.IsNameOnly(named) {
|
|
| 229 |
- named = reference.WithDefaultTag(named) |
|
| 230 |
- } |
|
| 231 |
- ref, ok := named.(reference.NamedTagged) |
|
| 232 |
- if !ok {
|
|
| 233 |
- return nil, fmt.Errorf("invalid name: %s", named.String())
|
|
| 234 |
- } |
|
| 235 |
- fullName = ref.String() |
|
| 236 |
- } |
|
| 237 |
- p, err = manager.get(fullName) |
|
| 238 |
- if err == nil {
|
|
| 239 |
- capability = strings.ToLower(capability) |
|
| 240 |
- for _, typ := range p.PluginObj.Manifest.Interface.Types {
|
|
| 241 |
- if typ.Capability == capability && typ.Prefix == "docker" {
|
|
| 242 |
- return p, nil |
|
| 243 |
- } |
|
| 244 |
- } |
|
| 245 |
- return nil, ErrInadequateCapability{name, capability}
|
|
| 246 |
- } |
|
| 247 |
- if _, ok := err.(ErrNotFound); !ok {
|
|
| 248 |
- return nil, err |
|
| 249 |
- } |
|
| 250 |
- } |
|
| 251 |
- |
|
| 252 |
- // Lookup using legacy model |
|
| 253 |
- if allowV1PluginsFallback {
|
|
| 254 |
- p, err := plugins.Get(name, capability) |
|
| 255 |
- if err != nil {
|
|
| 256 |
- return nil, fmt.Errorf("legacy plugin: %v", err)
|
|
| 257 |
- } |
|
| 258 |
- return p, nil |
|
| 259 |
- } |
|
| 260 |
- |
|
| 261 |
- return nil, err |
|
| 262 |
-} |
|
| 263 |
- |
|
| 264 | 165 |
// StateChanged updates plugin internals using libcontainerd events. |
| 265 | 166 |
func (pm *Manager) StateChanged(id string, e libcontainerd.StateInfo) error {
|
| 266 | 167 |
logrus.Debugf("plugin state changed %s %#v", id, e)
|
| ... | ... |
@@ -272,13 +109,11 @@ func (pm *Manager) StateChanged(id string, e libcontainerd.StateInfo) error {
|
| 272 | 272 |
shutdown = pm.shutdown |
| 273 | 273 |
pm.RUnlock() |
| 274 | 274 |
if shutdown {
|
| 275 |
- pm.RLock() |
|
| 276 |
- p, idOk := pm.plugins[id] |
|
| 277 |
- pm.RUnlock() |
|
| 278 |
- if !idOk {
|
|
| 279 |
- return ErrNotFound(id) |
|
| 275 |
+ p, err := pm.pluginStore.GetByID(id) |
|
| 276 |
+ if err != nil {
|
|
| 277 |
+ return err |
|
| 280 | 278 |
} |
| 281 |
- close(p.exitChan) |
|
| 279 |
+ close(p.ExitChan) |
|
| 282 | 280 |
} |
| 283 | 281 |
} |
| 284 | 282 |
|
| ... | ... |
@@ -313,24 +148,24 @@ func (pm *Manager) init() error {
|
| 313 | 313 |
} |
| 314 | 314 |
defer dt.Close() |
| 315 | 315 |
|
| 316 |
- if err := json.NewDecoder(dt).Decode(&pm.plugins); err != nil {
|
|
| 316 |
+ plugins := make(map[string]*v2.Plugin) |
|
| 317 |
+ if err := json.NewDecoder(dt).Decode(&plugins); err != nil {
|
|
| 317 | 318 |
return err |
| 318 | 319 |
} |
| 320 |
+ pm.pluginStore.SetAll(plugins) |
|
| 319 | 321 |
|
| 320 | 322 |
var group sync.WaitGroup |
| 321 |
- group.Add(len(pm.plugins)) |
|
| 322 |
- for _, p := range pm.plugins {
|
|
| 323 |
- go func(p *plugin) {
|
|
| 323 |
+ group.Add(len(plugins)) |
|
| 324 |
+ for _, p := range plugins {
|
|
| 325 |
+ go func(p *v2.Plugin) {
|
|
| 324 | 326 |
defer group.Done() |
| 325 | 327 |
if err := pm.restorePlugin(p); err != nil {
|
| 326 | 328 |
logrus.Errorf("failed to restore plugin '%s': %s", p.Name(), err)
|
| 327 | 329 |
return |
| 328 | 330 |
} |
| 329 | 331 |
|
| 330 |
- pm.Lock() |
|
| 331 |
- pm.nameToID[p.Name()] = p.PluginObj.ID |
|
| 332 |
- requiresManualRestore := !pm.liveRestore && p.PluginObj.Enabled |
|
| 333 |
- pm.Unlock() |
|
| 332 |
+ pm.pluginStore.Add(p) |
|
| 333 |
+ requiresManualRestore := !pm.liveRestore && p.IsEnabled() |
|
| 334 | 334 |
|
| 335 | 335 |
if requiresManualRestore {
|
| 336 | 336 |
// if liveRestore is not enabled, the plugin will be stopped now so we should enable it |
| ... | ... |
@@ -341,80 +176,6 @@ func (pm *Manager) init() error {
|
| 341 | 341 |
}(p) |
| 342 | 342 |
} |
| 343 | 343 |
group.Wait() |
| 344 |
- return pm.save() |
|
| 345 |
-} |
|
| 346 |
- |
|
| 347 |
-func (pm *Manager) initPlugin(p *plugin) error {
|
|
| 348 |
- dt, err := os.Open(filepath.Join(pm.libRoot, p.PluginObj.ID, "manifest.json")) |
|
| 349 |
- if err != nil {
|
|
| 350 |
- return err |
|
| 351 |
- } |
|
| 352 |
- err = json.NewDecoder(dt).Decode(&p.PluginObj.Manifest) |
|
| 353 |
- dt.Close() |
|
| 354 |
- if err != nil {
|
|
| 355 |
- return err |
|
| 356 |
- } |
|
| 357 |
- |
|
| 358 |
- p.PluginObj.Config.Mounts = make([]types.PluginMount, len(p.PluginObj.Manifest.Mounts)) |
|
| 359 |
- for i, mount := range p.PluginObj.Manifest.Mounts {
|
|
| 360 |
- p.PluginObj.Config.Mounts[i] = mount |
|
| 361 |
- } |
|
| 362 |
- p.PluginObj.Config.Env = make([]string, 0, len(p.PluginObj.Manifest.Env)) |
|
| 363 |
- for _, env := range p.PluginObj.Manifest.Env {
|
|
| 364 |
- if env.Value != nil {
|
|
| 365 |
- p.PluginObj.Config.Env = append(p.PluginObj.Config.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
|
|
| 366 |
- } |
|
| 367 |
- } |
|
| 368 |
- copy(p.PluginObj.Config.Args, p.PluginObj.Manifest.Args.Value) |
|
| 369 |
- |
|
| 370 |
- f, err := os.Create(filepath.Join(pm.libRoot, p.PluginObj.ID, "plugin-config.json")) |
|
| 371 |
- if err != nil {
|
|
| 372 |
- return err |
|
| 373 |
- } |
|
| 374 |
- err = json.NewEncoder(f).Encode(&p.PluginObj.Config) |
|
| 375 |
- f.Close() |
|
| 376 |
- return err |
|
| 377 |
-} |
|
| 378 |
- |
|
| 379 |
-func (pm *Manager) remove(p *plugin, force bool) error {
|
|
| 380 |
- if p.PluginObj.Enabled {
|
|
| 381 |
- if !force {
|
|
| 382 |
- return fmt.Errorf("plugin %s is enabled", p.Name())
|
|
| 383 |
- } |
|
| 384 |
- if err := pm.disable(p); err != nil {
|
|
| 385 |
- logrus.Errorf("failed to disable plugin '%s': %s", p.Name(), err)
|
|
| 386 |
- } |
|
| 387 |
- } |
|
| 388 |
- pm.Lock() // fixme: lock single record |
|
| 389 |
- defer pm.Unlock() |
|
| 390 |
- delete(pm.plugins, p.PluginObj.ID) |
|
| 391 |
- delete(pm.nameToID, p.Name()) |
|
| 392 |
- pm.save() |
|
| 393 |
- return os.RemoveAll(filepath.Join(pm.libRoot, p.PluginObj.ID)) |
|
| 394 |
-} |
|
| 395 |
- |
|
| 396 |
-func (pm *Manager) set(p *plugin, args []string) error {
|
|
| 397 |
- m := make(map[string]string, len(args)) |
|
| 398 |
- for _, arg := range args {
|
|
| 399 |
- i := strings.Index(arg, "=") |
|
| 400 |
- if i < 0 {
|
|
| 401 |
- return fmt.Errorf("no equal sign '=' found in %s", arg)
|
|
| 402 |
- } |
|
| 403 |
- m[arg[:i]] = arg[i+1:] |
|
| 404 |
- } |
|
| 405 |
- return errors.New("not implemented")
|
|
| 406 |
-} |
|
| 407 |
- |
|
| 408 |
-// fixme: not safe |
|
| 409 |
-func (pm *Manager) save() error {
|
|
| 410 |
- filePath := filepath.Join(pm.libRoot, "plugins.json") |
|
| 411 |
- |
|
| 412 |
- jsonData, err := json.Marshal(pm.plugins) |
|
| 413 |
- if err != nil {
|
|
| 414 |
- logrus.Debugf("failure in json.Marshal: %v", err)
|
|
| 415 |
- return err |
|
| 416 |
- } |
|
| 417 |
- ioutils.AtomicWriteFile(filePath, jsonData, 0600) |
|
| 418 | 344 |
return nil |
| 419 | 345 |
} |
| 420 | 346 |
|
| ... | ... |
@@ -428,40 +189,3 @@ func (l logHook) Fire(entry *logrus.Entry) error {
|
| 428 | 428 |
entry.Data = logrus.Fields{"plugin": l.id}
|
| 429 | 429 |
return nil |
| 430 | 430 |
} |
| 431 |
- |
|
| 432 |
-func computePrivileges(m *types.PluginManifest) types.PluginPrivileges {
|
|
| 433 |
- var privileges types.PluginPrivileges |
|
| 434 |
- if m.Network.Type != "null" && m.Network.Type != "bridge" {
|
|
| 435 |
- privileges = append(privileges, types.PluginPrivilege{
|
|
| 436 |
- Name: "network", |
|
| 437 |
- Description: "", |
|
| 438 |
- Value: []string{m.Network.Type},
|
|
| 439 |
- }) |
|
| 440 |
- } |
|
| 441 |
- for _, mount := range m.Mounts {
|
|
| 442 |
- if mount.Source != nil {
|
|
| 443 |
- privileges = append(privileges, types.PluginPrivilege{
|
|
| 444 |
- Name: "mount", |
|
| 445 |
- Description: "", |
|
| 446 |
- Value: []string{*mount.Source},
|
|
| 447 |
- }) |
|
| 448 |
- } |
|
| 449 |
- } |
|
| 450 |
- for _, device := range m.Devices {
|
|
| 451 |
- if device.Path != nil {
|
|
| 452 |
- privileges = append(privileges, types.PluginPrivilege{
|
|
| 453 |
- Name: "device", |
|
| 454 |
- Description: "", |
|
| 455 |
- Value: []string{*device.Path},
|
|
| 456 |
- }) |
|
| 457 |
- } |
|
| 458 |
- } |
|
| 459 |
- if len(m.Capabilities) > 0 {
|
|
| 460 |
- privileges = append(privileges, types.PluginPrivilege{
|
|
| 461 |
- Name: "capabilities", |
|
| 462 |
- Description: "", |
|
| 463 |
- Value: m.Capabilities, |
|
| 464 |
- }) |
|
| 465 |
- } |
|
| 466 |
- return privileges |
|
| 467 |
-} |
| ... | ... |
@@ -4,7 +4,6 @@ package plugin |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 | 6 |
"fmt" |
| 7 |
- "os" |
|
| 8 | 7 |
"path/filepath" |
| 9 | 8 |
"syscall" |
| 10 | 9 |
"time" |
| ... | ... |
@@ -13,45 +12,38 @@ import ( |
| 13 | 13 |
"github.com/docker/docker/libcontainerd" |
| 14 | 14 |
"github.com/docker/docker/oci" |
| 15 | 15 |
"github.com/docker/docker/pkg/plugins" |
| 16 |
- "github.com/docker/docker/pkg/system" |
|
| 16 |
+ "github.com/docker/docker/plugin/v2" |
|
| 17 | 17 |
"github.com/docker/docker/restartmanager" |
| 18 |
- "github.com/docker/engine-api/types" |
|
| 19 | 18 |
"github.com/docker/engine-api/types/container" |
| 20 |
- "github.com/opencontainers/runtime-spec/specs-go" |
|
| 21 | 19 |
) |
| 22 | 20 |
|
| 23 |
-func (pm *Manager) enable(p *plugin, force bool) error {
|
|
| 24 |
- if p.PluginObj.Enabled && !force {
|
|
| 21 |
+func (pm *Manager) enable(p *v2.Plugin, force bool) error {
|
|
| 22 |
+ if p.IsEnabled() && !force {
|
|
| 25 | 23 |
return fmt.Errorf("plugin %s is already enabled", p.Name())
|
| 26 | 24 |
} |
| 27 |
- spec, err := pm.initSpec(p) |
|
| 25 |
+ spec, err := p.InitSpec(oci.DefaultSpec(), pm.libRoot) |
|
| 28 | 26 |
if err != nil {
|
| 29 | 27 |
return err |
| 30 | 28 |
} |
| 31 | 29 |
|
| 32 |
- p.restartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
|
|
| 33 |
- if err := pm.containerdClient.Create(p.PluginObj.ID, libcontainerd.Spec(*spec), libcontainerd.WithRestartManager(p.restartManager)); err != nil { // POC-only
|
|
| 34 |
- if err := p.restartManager.Cancel(); err != nil {
|
|
| 30 |
+ p.RestartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
|
|
| 31 |
+ if err := pm.containerdClient.Create(p.GetID(), libcontainerd.Spec(*spec), libcontainerd.WithRestartManager(p.RestartManager)); err != nil {
|
|
| 32 |
+ if err := p.RestartManager.Cancel(); err != nil {
|
|
| 35 | 33 |
logrus.Errorf("enable: restartManager.Cancel failed due to %v", err)
|
| 36 | 34 |
} |
| 37 | 35 |
return err |
| 38 | 36 |
} |
| 39 | 37 |
|
| 40 |
- socket := p.PluginObj.Manifest.Interface.Socket |
|
| 41 |
- p.client, err = plugins.NewClient("unix://"+filepath.Join(p.runtimeSourcePath, socket), nil)
|
|
| 38 |
+ p.PClient, err = plugins.NewClient("unix://"+filepath.Join(p.RuntimeSourcePath, p.GetSocket()), nil)
|
|
| 42 | 39 |
if err != nil {
|
| 43 |
- if err := p.restartManager.Cancel(); err != nil {
|
|
| 40 |
+ if err := p.RestartManager.Cancel(); err != nil {
|
|
| 44 | 41 |
logrus.Errorf("enable: restartManager.Cancel failed due to %v", err)
|
| 45 | 42 |
} |
| 46 | 43 |
return err |
| 47 | 44 |
} |
| 48 | 45 |
|
| 49 |
- pm.Lock() // fixme: lock single record |
|
| 50 |
- p.PluginObj.Enabled = true |
|
| 51 |
- pm.save() |
|
| 52 |
- pm.Unlock() |
|
| 53 |
- |
|
| 54 |
- for _, typ := range p.PluginObj.Manifest.Interface.Types {
|
|
| 46 |
+ pm.pluginStore.SetState(p, true) |
|
| 47 |
+ for _, typ := range p.GetTypes() {
|
|
| 55 | 48 |
if handler := pm.handlers[typ.String()]; handler != nil {
|
| 56 | 49 |
handler(p.Name(), p.Client()) |
| 57 | 50 |
} |
| ... | ... |
@@ -60,90 +52,25 @@ func (pm *Manager) enable(p *plugin, force bool) error {
|
| 60 | 60 |
return nil |
| 61 | 61 |
} |
| 62 | 62 |
|
| 63 |
-func (pm *Manager) restore(p *plugin) error {
|
|
| 64 |
- p.restartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
|
|
| 65 |
- return pm.containerdClient.Restore(p.PluginObj.ID, libcontainerd.WithRestartManager(p.restartManager)) |
|
| 63 |
+func (pm *Manager) restore(p *v2.Plugin) error {
|
|
| 64 |
+ p.RestartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
|
|
| 65 |
+ return pm.containerdClient.Restore(p.GetID(), libcontainerd.WithRestartManager(p.RestartManager)) |
|
| 66 | 66 |
} |
| 67 | 67 |
|
| 68 |
-func (pm *Manager) initSpec(p *plugin) (*specs.Spec, error) {
|
|
| 69 |
- s := oci.DefaultSpec() |
|
| 70 |
- |
|
| 71 |
- rootfs := filepath.Join(pm.libRoot, p.PluginObj.ID, "rootfs") |
|
| 72 |
- s.Root = specs.Root{
|
|
| 73 |
- Path: rootfs, |
|
| 74 |
- Readonly: false, // TODO: all plugins should be readonly? settable in manifest? |
|
| 75 |
- } |
|
| 76 |
- |
|
| 77 |
- mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
|
|
| 78 |
- Source: &p.runtimeSourcePath, |
|
| 79 |
- Destination: defaultPluginRuntimeDestination, |
|
| 80 |
- Type: "bind", |
|
| 81 |
- Options: []string{"rbind", "rshared"},
|
|
| 82 |
- }) |
|
| 83 |
- for _, mount := range mounts {
|
|
| 84 |
- m := specs.Mount{
|
|
| 85 |
- Destination: mount.Destination, |
|
| 86 |
- Type: mount.Type, |
|
| 87 |
- Options: mount.Options, |
|
| 88 |
- } |
|
| 89 |
- // TODO: if nil, then it's required and user didn't set it |
|
| 90 |
- if mount.Source != nil {
|
|
| 91 |
- m.Source = *mount.Source |
|
| 92 |
- } |
|
| 93 |
- |
|
| 94 |
- if m.Source != "" && m.Type == "bind" {
|
|
| 95 |
- /* Debugging issue #25511: Volumes and other content created under the |
|
| 96 |
- bind mount should be recursively propagated. rshared, not shared. |
|
| 97 |
- This could be the reason for EBUSY during removal. Override options |
|
| 98 |
- with rbind, rshared and see if CI errors are fixed. */ |
|
| 99 |
- m.Options = []string{"rbind", "rshared"}
|
|
| 100 |
- fi, err := os.Lstat(filepath.Join(rootfs, string(os.PathSeparator), m.Destination)) // TODO: followsymlinks |
|
| 101 |
- if err != nil {
|
|
| 102 |
- return nil, err |
|
| 103 |
- } |
|
| 104 |
- if fi.IsDir() {
|
|
| 105 |
- if err := os.MkdirAll(m.Source, 0700); err != nil {
|
|
| 106 |
- return nil, err |
|
| 107 |
- } |
|
| 108 |
- } |
|
| 109 |
- } |
|
| 110 |
- s.Mounts = append(s.Mounts, m) |
|
| 111 |
- } |
|
| 112 |
- |
|
| 113 |
- envs := make([]string, 1, len(p.PluginObj.Config.Env)+1) |
|
| 114 |
- envs[0] = "PATH=" + system.DefaultPathEnv |
|
| 115 |
- envs = append(envs, p.PluginObj.Config.Env...) |
|
| 116 |
- |
|
| 117 |
- args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...) |
|
| 118 |
- cwd := p.PluginObj.Manifest.Workdir |
|
| 119 |
- if len(cwd) == 0 {
|
|
| 120 |
- cwd = "/" |
|
| 121 |
- } |
|
| 122 |
- s.Process = specs.Process{
|
|
| 123 |
- Terminal: false, |
|
| 124 |
- Args: args, |
|
| 125 |
- Cwd: cwd, |
|
| 126 |
- Env: envs, |
|
| 127 |
- } |
|
| 128 |
- |
|
| 129 |
- return &s, nil |
|
| 130 |
-} |
|
| 131 |
- |
|
| 132 |
-func (pm *Manager) disable(p *plugin) error {
|
|
| 133 |
- if !p.PluginObj.Enabled {
|
|
| 68 |
+func (pm *Manager) disable(p *v2.Plugin) error {
|
|
| 69 |
+ if !p.IsEnabled() {
|
|
| 134 | 70 |
return fmt.Errorf("plugin %s is already disabled", p.Name())
|
| 135 | 71 |
} |
| 136 |
- if err := p.restartManager.Cancel(); err != nil {
|
|
| 72 |
+ if err := p.RestartManager.Cancel(); err != nil {
|
|
| 73 |
+ logrus.Error(err) |
|
| 74 |
+ } |
|
| 75 |
+ if err := pm.containerdClient.Signal(p.GetID(), int(syscall.SIGKILL)); err != nil {
|
|
| 137 | 76 |
logrus.Error(err) |
| 138 | 77 |
} |
| 139 |
- if err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGKILL)); err != nil {
|
|
| 78 |
+ if err := p.RemoveFromDisk(); err != nil {
|
|
| 140 | 79 |
logrus.Error(err) |
| 141 | 80 |
} |
| 142 |
- os.RemoveAll(p.runtimeSourcePath) |
|
| 143 |
- pm.Lock() // fixme: lock single record |
|
| 144 |
- defer pm.Unlock() |
|
| 145 |
- p.PluginObj.Enabled = false |
|
| 146 |
- pm.save() |
|
| 81 |
+ pm.pluginStore.SetState(p, false) |
|
| 147 | 82 |
return nil |
| 148 | 83 |
} |
| 149 | 84 |
|
| ... | ... |
@@ -155,34 +82,36 @@ func (pm *Manager) Shutdown() {
|
| 155 | 155 |
|
| 156 | 156 |
pm.RLock() |
| 157 | 157 |
defer pm.RUnlock() |
| 158 |
- for _, p := range pm.plugins {
|
|
| 159 |
- if pm.liveRestore && p.PluginObj.Enabled {
|
|
| 160 |
- logrus.Debug("Plugin enabled when liveRestore is set, skipping shutdown")
|
|
| 158 |
+ plugins := pm.pluginStore.GetAll() |
|
| 159 |
+ for _, p := range plugins {
|
|
| 160 |
+ if pm.liveRestore && p.IsEnabled() {
|
|
| 161 |
+ logrus.Debug("Plugin active when liveRestore is set, skipping shutdown")
|
|
| 161 | 162 |
continue |
| 162 | 163 |
} |
| 163 |
- if p.restartManager != nil {
|
|
| 164 |
- if err := p.restartManager.Cancel(); err != nil {
|
|
| 164 |
+ if p.RestartManager != nil {
|
|
| 165 |
+ if err := p.RestartManager.Cancel(); err != nil {
|
|
| 165 | 166 |
logrus.Error(err) |
| 166 | 167 |
} |
| 167 | 168 |
} |
| 168 |
- if pm.containerdClient != nil && p.PluginObj.Enabled {
|
|
| 169 |
- p.exitChan = make(chan bool) |
|
| 169 |
+ if pm.containerdClient != nil && p.IsEnabled() {
|
|
| 170 |
+ pluginID := p.GetID() |
|
| 171 |
+ p.ExitChan = make(chan bool) |
|
| 170 | 172 |
err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGTERM)) |
| 171 | 173 |
if err != nil {
|
| 172 | 174 |
logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
|
| 173 | 175 |
} else {
|
| 174 | 176 |
select {
|
| 175 |
- case <-p.exitChan: |
|
| 177 |
+ case <-p.ExitChan: |
|
| 176 | 178 |
logrus.Debug("Clean shutdown of plugin")
|
| 177 | 179 |
case <-time.After(time.Second * 10): |
| 178 | 180 |
logrus.Debug("Force shutdown plugin")
|
| 179 |
- if err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGKILL)); err != nil {
|
|
| 181 |
+ if err := pm.containerdClient.Signal(pluginID, int(syscall.SIGKILL)); err != nil {
|
|
| 180 | 182 |
logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
|
| 181 | 183 |
} |
| 182 | 184 |
} |
| 183 | 185 |
} |
| 184 | 186 |
} |
| 185 |
- if err := os.RemoveAll(p.runtimeSourcePath); err != nil {
|
|
| 187 |
+ if err := p.RemoveFromDisk(); err != nil {
|
|
| 186 | 188 |
logrus.Errorf("Remove plugin runtime failed with error: %v", err)
|
| 187 | 189 |
} |
| 188 | 190 |
} |
| ... | ... |
@@ -5,22 +5,23 @@ package plugin |
| 5 | 5 |
import ( |
| 6 | 6 |
"fmt" |
| 7 | 7 |
|
| 8 |
+ "github.com/docker/docker/plugin/v2" |
|
| 8 | 9 |
"github.com/opencontainers/runtime-spec/specs-go" |
| 9 | 10 |
) |
| 10 | 11 |
|
| 11 |
-func (pm *Manager) enable(p *plugin, force bool) error {
|
|
| 12 |
+func (pm *Manager) enable(p *v2.Plugin, force bool) error {
|
|
| 12 | 13 |
return fmt.Errorf("Not implemented")
|
| 13 | 14 |
} |
| 14 | 15 |
|
| 15 |
-func (pm *Manager) initSpec(p *plugin) (*specs.Spec, error) {
|
|
| 16 |
+func (pm *Manager) initSpec(p *v2.Plugin) (*specs.Spec, error) {
|
|
| 16 | 17 |
return nil, fmt.Errorf("Not implemented")
|
| 17 | 18 |
} |
| 18 | 19 |
|
| 19 |
-func (pm *Manager) disable(p *plugin) error {
|
|
| 20 |
+func (pm *Manager) disable(p *v2.Plugin) error {
|
|
| 20 | 21 |
return fmt.Errorf("Not implemented")
|
| 21 | 22 |
} |
| 22 | 23 |
|
| 23 |
-func (pm *Manager) restore(p *plugin) error {
|
|
| 24 |
+func (pm *Manager) restore(p *v2.Plugin) error {
|
|
| 24 | 25 |
return fmt.Errorf("Not implemented")
|
| 25 | 26 |
} |
| 26 | 27 |
|
| 27 | 28 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,10 @@ |
| 0 |
+package store |
|
| 1 |
+ |
|
| 2 |
+import "github.com/docker/docker/pkg/plugins" |
|
| 3 |
+ |
|
| 4 |
+// CompatPlugin is a abstraction to handle both new and legacy (v1) plugins. |
|
| 5 |
+type CompatPlugin interface {
|
|
| 6 |
+ Client() *plugins.Client |
|
| 7 |
+ Name() string |
|
| 8 |
+ IsLegacy() bool |
|
| 9 |
+} |
| 0 | 10 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,25 @@ |
| 0 |
+// +build !experimental |
|
| 1 |
+ |
|
| 2 |
+package store |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "github.com/docker/docker/pkg/plugins" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+// FindWithCapability returns a list of plugins matching the given capability. |
|
| 9 |
+func FindWithCapability(capability string) ([]CompatPlugin, error) {
|
|
| 10 |
+ pl, err := plugins.GetAll(capability) |
|
| 11 |
+ if err != nil {
|
|
| 12 |
+ return nil, err |
|
| 13 |
+ } |
|
| 14 |
+ result := make([]CompatPlugin, len(pl)) |
|
| 15 |
+ for i, p := range pl {
|
|
| 16 |
+ result[i] = p |
|
| 17 |
+ } |
|
| 18 |
+ return result, nil |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+// LookupWithCapability returns a plugin matching the given name and capability. |
|
| 22 |
+func LookupWithCapability(name, capability string) (CompatPlugin, error) {
|
|
| 23 |
+ return plugins.Get(name, capability) |
|
| 24 |
+} |
| 0 | 25 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,224 @@ |
| 0 |
+// +build experimental |
|
| 1 |
+ |
|
| 2 |
+package store |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "encoding/json" |
|
| 6 |
+ "fmt" |
|
| 7 |
+ "path/filepath" |
|
| 8 |
+ "sync" |
|
| 9 |
+ |
|
| 10 |
+ "github.com/Sirupsen/logrus" |
|
| 11 |
+ "github.com/docker/docker/pkg/ioutils" |
|
| 12 |
+ "github.com/docker/docker/pkg/plugins" |
|
| 13 |
+ "github.com/docker/docker/plugin/v2" |
|
| 14 |
+ "github.com/docker/docker/reference" |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+var ( |
|
| 18 |
+ store *PluginStore |
|
| 19 |
+ /* allowV1PluginsFallback determines daemon's support for V1 plugins. |
|
| 20 |
+ * When the time comes to remove support for V1 plugins, flipping |
|
| 21 |
+ * this bool is all that will be needed. |
|
| 22 |
+ */ |
|
| 23 |
+ allowV1PluginsFallback = true |
|
| 24 |
+) |
|
| 25 |
+ |
|
| 26 |
+// ErrNotFound indicates that a plugin was not found locally. |
|
| 27 |
+type ErrNotFound string |
|
| 28 |
+ |
|
| 29 |
+func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }
|
|
| 30 |
+ |
|
| 31 |
+// PluginStore manages the plugin inventory in memory and on-disk |
|
| 32 |
+type PluginStore struct {
|
|
| 33 |
+ sync.RWMutex |
|
| 34 |
+ plugins map[string]*v2.Plugin |
|
| 35 |
+ nameToID map[string]string |
|
| 36 |
+ plugindb string |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+// NewPluginStore creates a PluginStore. |
|
| 40 |
+func NewPluginStore(libRoot string) *PluginStore {
|
|
| 41 |
+ store = &PluginStore{
|
|
| 42 |
+ plugins: make(map[string]*v2.Plugin), |
|
| 43 |
+ nameToID: make(map[string]string), |
|
| 44 |
+ plugindb: filepath.Join(libRoot, "plugins.json"), |
|
| 45 |
+ } |
|
| 46 |
+ return store |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+// GetByName retreives a plugin by name. |
|
| 50 |
+func (ps *PluginStore) GetByName(name string) (*v2.Plugin, error) {
|
|
| 51 |
+ ps.RLock() |
|
| 52 |
+ defer ps.RUnlock() |
|
| 53 |
+ |
|
| 54 |
+ id, nameOk := ps.nameToID[name] |
|
| 55 |
+ if !nameOk {
|
|
| 56 |
+ return nil, ErrNotFound(name) |
|
| 57 |
+ } |
|
| 58 |
+ |
|
| 59 |
+ p, idOk := ps.plugins[id] |
|
| 60 |
+ if !idOk {
|
|
| 61 |
+ return nil, ErrNotFound(id) |
|
| 62 |
+ } |
|
| 63 |
+ return p, nil |
|
| 64 |
+} |
|
| 65 |
+ |
|
| 66 |
+// GetByID retreives a plugin by ID. |
|
| 67 |
+func (ps *PluginStore) GetByID(id string) (*v2.Plugin, error) {
|
|
| 68 |
+ ps.RLock() |
|
| 69 |
+ defer ps.RUnlock() |
|
| 70 |
+ |
|
| 71 |
+ p, idOk := ps.plugins[id] |
|
| 72 |
+ if !idOk {
|
|
| 73 |
+ return nil, ErrNotFound(id) |
|
| 74 |
+ } |
|
| 75 |
+ return p, nil |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+// GetAll retreives all plugins. |
|
| 79 |
+func (ps *PluginStore) GetAll() map[string]*v2.Plugin {
|
|
| 80 |
+ ps.RLock() |
|
| 81 |
+ defer ps.RUnlock() |
|
| 82 |
+ return ps.plugins |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+// SetAll initialized plugins during daemon restore. |
|
| 86 |
+func (ps *PluginStore) SetAll(plugins map[string]*v2.Plugin) {
|
|
| 87 |
+ ps.Lock() |
|
| 88 |
+ defer ps.Unlock() |
|
| 89 |
+ ps.plugins = plugins |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+func (ps *PluginStore) getByCap(name string, capability string) (*v2.Plugin, error) {
|
|
| 93 |
+ ps.RLock() |
|
| 94 |
+ defer ps.RUnlock() |
|
| 95 |
+ |
|
| 96 |
+ p, err := ps.GetByName(name) |
|
| 97 |
+ if err != nil {
|
|
| 98 |
+ return nil, err |
|
| 99 |
+ } |
|
| 100 |
+ return p.FilterByCap(capability) |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+func (ps *PluginStore) getAllByCap(capability string) []CompatPlugin {
|
|
| 104 |
+ ps.RLock() |
|
| 105 |
+ defer ps.RUnlock() |
|
| 106 |
+ |
|
| 107 |
+ result := make([]CompatPlugin, 0, 1) |
|
| 108 |
+ for _, p := range ps.plugins {
|
|
| 109 |
+ if _, err := p.FilterByCap(capability); err == nil {
|
|
| 110 |
+ result = append(result, p) |
|
| 111 |
+ } |
|
| 112 |
+ } |
|
| 113 |
+ return result |
|
| 114 |
+} |
|
| 115 |
+ |
|
| 116 |
+// SetState sets the active state of the plugin and updates plugindb. |
|
| 117 |
+func (ps *PluginStore) SetState(p *v2.Plugin, state bool) {
|
|
| 118 |
+ ps.Lock() |
|
| 119 |
+ defer ps.Unlock() |
|
| 120 |
+ |
|
| 121 |
+ p.PluginObj.Enabled = state |
|
| 122 |
+ ps.updatePluginDB() |
|
| 123 |
+} |
|
| 124 |
+ |
|
| 125 |
+// Add adds a plugin to memory and plugindb. |
|
| 126 |
+func (ps *PluginStore) Add(p *v2.Plugin) {
|
|
| 127 |
+ ps.Lock() |
|
| 128 |
+ ps.plugins[p.GetID()] = p |
|
| 129 |
+ ps.nameToID[p.Name()] = p.GetID() |
|
| 130 |
+ ps.updatePluginDB() |
|
| 131 |
+ ps.Unlock() |
|
| 132 |
+} |
|
| 133 |
+ |
|
| 134 |
+// Remove removes a plugin from memory, plugindb and disk. |
|
| 135 |
+func (ps *PluginStore) Remove(p *v2.Plugin) {
|
|
| 136 |
+ ps.Lock() |
|
| 137 |
+ delete(ps.plugins, p.GetID()) |
|
| 138 |
+ delete(ps.nameToID, p.Name()) |
|
| 139 |
+ ps.updatePluginDB() |
|
| 140 |
+ p.RemoveFromDisk() |
|
| 141 |
+ ps.Unlock() |
|
| 142 |
+} |
|
| 143 |
+ |
|
| 144 |
+// Callers are expected to hold the store lock. |
|
| 145 |
+func (ps *PluginStore) updatePluginDB() error {
|
|
| 146 |
+ jsonData, err := json.Marshal(ps.plugins) |
|
| 147 |
+ if err != nil {
|
|
| 148 |
+ logrus.Debugf("Error in json.Marshal: %v", err)
|
|
| 149 |
+ return err |
|
| 150 |
+ } |
|
| 151 |
+ ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600) |
|
| 152 |
+ return nil |
|
| 153 |
+} |
|
| 154 |
+ |
|
| 155 |
+// LookupWithCapability returns a plugin matching the given name and capability. |
|
| 156 |
+func LookupWithCapability(name, capability string) (CompatPlugin, error) {
|
|
| 157 |
+ var ( |
|
| 158 |
+ p *v2.Plugin |
|
| 159 |
+ err error |
|
| 160 |
+ ) |
|
| 161 |
+ |
|
| 162 |
+ // Lookup using new model. |
|
| 163 |
+ if store != nil {
|
|
| 164 |
+ fullName := name |
|
| 165 |
+ if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
|
|
| 166 |
+ if reference.IsNameOnly(named) {
|
|
| 167 |
+ named = reference.WithDefaultTag(named) |
|
| 168 |
+ } |
|
| 169 |
+ ref, ok := named.(reference.NamedTagged) |
|
| 170 |
+ if !ok {
|
|
| 171 |
+ return nil, fmt.Errorf("invalid name: %s", named.String())
|
|
| 172 |
+ } |
|
| 173 |
+ fullName = ref.String() |
|
| 174 |
+ } |
|
| 175 |
+ p, err = store.GetByName(fullName) |
|
| 176 |
+ if err == nil {
|
|
| 177 |
+ return p.FilterByCap(capability) |
|
| 178 |
+ } |
|
| 179 |
+ if _, ok := err.(ErrNotFound); !ok {
|
|
| 180 |
+ return nil, err |
|
| 181 |
+ } |
|
| 182 |
+ } |
|
| 183 |
+ |
|
| 184 |
+ // Lookup using legacy model. |
|
| 185 |
+ if allowV1PluginsFallback {
|
|
| 186 |
+ p, err := plugins.Get(name, capability) |
|
| 187 |
+ if err != nil {
|
|
| 188 |
+ return nil, fmt.Errorf("legacy plugin: %v", err)
|
|
| 189 |
+ } |
|
| 190 |
+ return p, nil |
|
| 191 |
+ } |
|
| 192 |
+ |
|
| 193 |
+ return nil, err |
|
| 194 |
+} |
|
| 195 |
+ |
|
| 196 |
+// FindWithCapability returns a list of plugins matching the given capability. |
|
| 197 |
+func FindWithCapability(capability string) ([]CompatPlugin, error) {
|
|
| 198 |
+ result := make([]CompatPlugin, 0, 1) |
|
| 199 |
+ |
|
| 200 |
+ /* Daemon start always calls plugin.Init thereby initializing a store. |
|
| 201 |
+ * So store on experimental builds can never be nil, even while |
|
| 202 |
+ * handling legacy plugins. However, there are legacy plugin unit |
|
| 203 |
+ * tests where the volume subsystem directly talks with the plugin, |
|
| 204 |
+ * bypassing the daemon. For such tests, this check is necessary. |
|
| 205 |
+ */ |
|
| 206 |
+ if store != nil {
|
|
| 207 |
+ store.RLock() |
|
| 208 |
+ result = store.getAllByCap(capability) |
|
| 209 |
+ store.RUnlock() |
|
| 210 |
+ } |
|
| 211 |
+ |
|
| 212 |
+ // Lookup with legacy model |
|
| 213 |
+ if allowV1PluginsFallback {
|
|
| 214 |
+ pl, err := plugins.GetAll(capability) |
|
| 215 |
+ if err != nil {
|
|
| 216 |
+ return nil, fmt.Errorf("legacy plugin: %v", err)
|
|
| 217 |
+ } |
|
| 218 |
+ for _, p := range pl {
|
|
| 219 |
+ result = append(result, p) |
|
| 220 |
+ } |
|
| 221 |
+ } |
|
| 222 |
+ return result, nil |
|
| 223 |
+} |
| 0 | 224 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,261 @@ |
| 0 |
+// +build experimental |
|
| 1 |
+ |
|
| 2 |
+package v2 |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "encoding/json" |
|
| 6 |
+ "errors" |
|
| 7 |
+ "fmt" |
|
| 8 |
+ "os" |
|
| 9 |
+ "path/filepath" |
|
| 10 |
+ "strings" |
|
| 11 |
+ "sync" |
|
| 12 |
+ |
|
| 13 |
+ "github.com/docker/docker/pkg/plugins" |
|
| 14 |
+ "github.com/docker/docker/pkg/system" |
|
| 15 |
+ "github.com/docker/docker/restartmanager" |
|
| 16 |
+ "github.com/docker/engine-api/types" |
|
| 17 |
+ "github.com/opencontainers/runtime-spec/specs-go" |
|
| 18 |
+) |
|
| 19 |
+ |
|
| 20 |
+const defaultPluginRuntimeDestination = "/run/docker/plugins" |
|
| 21 |
+ |
|
| 22 |
+// ErrInadequateCapability indicates that the plugin did not have the requested capability. |
|
| 23 |
+type ErrInadequateCapability string |
|
| 24 |
+ |
|
| 25 |
+func (cap ErrInadequateCapability) Error() string {
|
|
| 26 |
+ return fmt.Sprintf("plugin does not provide %q capability", cap)
|
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+// Plugin represents an individual plugin. |
|
| 30 |
+type Plugin struct {
|
|
| 31 |
+ sync.RWMutex |
|
| 32 |
+ PluginObj types.Plugin `json:"plugin"` |
|
| 33 |
+ PClient *plugins.Client `json:"-"` |
|
| 34 |
+ RestartManager restartmanager.RestartManager `json:"-"` |
|
| 35 |
+ RuntimeSourcePath string `json:"-"` |
|
| 36 |
+ ExitChan chan bool `json:"-"` |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func newPluginObj(name, id, tag string) types.Plugin {
|
|
| 40 |
+ return types.Plugin{Name: name, ID: id, Tag: tag}
|
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+// NewPlugin creates a plugin. |
|
| 44 |
+func NewPlugin(name, id, runRoot, tag string) *Plugin {
|
|
| 45 |
+ return &Plugin{
|
|
| 46 |
+ PluginObj: newPluginObj(name, id, tag), |
|
| 47 |
+ RuntimeSourcePath: filepath.Join(runRoot, id), |
|
| 48 |
+ } |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+// Client returns the plugin client. |
|
| 52 |
+func (p *Plugin) Client() *plugins.Client {
|
|
| 53 |
+ return p.PClient |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+// IsLegacy returns true for legacy plugins and false otherwise. |
|
| 57 |
+func (p *Plugin) IsLegacy() bool {
|
|
| 58 |
+ return false |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+// Name returns the plugin name. |
|
| 62 |
+func (p *Plugin) Name() string {
|
|
| 63 |
+ name := p.PluginObj.Name |
|
| 64 |
+ if len(p.PluginObj.Tag) > 0 {
|
|
| 65 |
+ // TODO: this feels hacky, maybe we should be storing the distribution reference rather than splitting these |
|
| 66 |
+ name += ":" + p.PluginObj.Tag |
|
| 67 |
+ } |
|
| 68 |
+ return name |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+// FilterByCap query the plugin for a given capability. |
|
| 72 |
+func (p *Plugin) FilterByCap(capability string) (*Plugin, error) {
|
|
| 73 |
+ capability = strings.ToLower(capability) |
|
| 74 |
+ for _, typ := range p.PluginObj.Manifest.Interface.Types {
|
|
| 75 |
+ if typ.Capability == capability && typ.Prefix == "docker" {
|
|
| 76 |
+ return p, nil |
|
| 77 |
+ } |
|
| 78 |
+ } |
|
| 79 |
+ return nil, ErrInadequateCapability(capability) |
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+// RemoveFromDisk deletes the plugin's runtime files from disk. |
|
| 83 |
+func (p *Plugin) RemoveFromDisk() error {
|
|
| 84 |
+ return os.RemoveAll(p.RuntimeSourcePath) |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+// InitPlugin populates the plugin object from the plugin manifest file. |
|
| 88 |
+func (p *Plugin) InitPlugin(libRoot string) error {
|
|
| 89 |
+ dt, err := os.Open(filepath.Join(libRoot, p.PluginObj.ID, "manifest.json")) |
|
| 90 |
+ if err != nil {
|
|
| 91 |
+ return err |
|
| 92 |
+ } |
|
| 93 |
+ err = json.NewDecoder(dt).Decode(&p.PluginObj.Manifest) |
|
| 94 |
+ dt.Close() |
|
| 95 |
+ if err != nil {
|
|
| 96 |
+ return err |
|
| 97 |
+ } |
|
| 98 |
+ |
|
| 99 |
+ p.PluginObj.Config.Mounts = make([]types.PluginMount, len(p.PluginObj.Manifest.Mounts)) |
|
| 100 |
+ for i, mount := range p.PluginObj.Manifest.Mounts {
|
|
| 101 |
+ p.PluginObj.Config.Mounts[i] = mount |
|
| 102 |
+ } |
|
| 103 |
+ p.PluginObj.Config.Env = make([]string, 0, len(p.PluginObj.Manifest.Env)) |
|
| 104 |
+ for _, env := range p.PluginObj.Manifest.Env {
|
|
| 105 |
+ if env.Value != nil {
|
|
| 106 |
+ p.PluginObj.Config.Env = append(p.PluginObj.Config.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
|
|
| 107 |
+ } |
|
| 108 |
+ } |
|
| 109 |
+ copy(p.PluginObj.Config.Args, p.PluginObj.Manifest.Args.Value) |
|
| 110 |
+ |
|
| 111 |
+ f, err := os.Create(filepath.Join(libRoot, p.PluginObj.ID, "plugin-config.json")) |
|
| 112 |
+ if err != nil {
|
|
| 113 |
+ return err |
|
| 114 |
+ } |
|
| 115 |
+ err = json.NewEncoder(f).Encode(&p.PluginObj.Config) |
|
| 116 |
+ f.Close() |
|
| 117 |
+ return err |
|
| 118 |
+} |
|
| 119 |
+ |
|
| 120 |
+// Set is used to pass arguments to the plugin. |
|
| 121 |
+func (p *Plugin) Set(args []string) error {
|
|
| 122 |
+ m := make(map[string]string, len(args)) |
|
| 123 |
+ for _, arg := range args {
|
|
| 124 |
+ i := strings.Index(arg, "=") |
|
| 125 |
+ if i < 0 {
|
|
| 126 |
+ return fmt.Errorf("No equal sign '=' found in %s", arg)
|
|
| 127 |
+ } |
|
| 128 |
+ m[arg[:i]] = arg[i+1:] |
|
| 129 |
+ } |
|
| 130 |
+ return errors.New("not implemented")
|
|
| 131 |
+} |
|
| 132 |
+ |
|
| 133 |
+// ComputePrivileges takes the manifest file and computes the list of access necessary |
|
| 134 |
+// for the plugin on the host. |
|
| 135 |
+func (p *Plugin) ComputePrivileges() types.PluginPrivileges {
|
|
| 136 |
+ m := p.PluginObj.Manifest |
|
| 137 |
+ var privileges types.PluginPrivileges |
|
| 138 |
+ if m.Network.Type != "null" && m.Network.Type != "bridge" {
|
|
| 139 |
+ privileges = append(privileges, types.PluginPrivilege{
|
|
| 140 |
+ Name: "network", |
|
| 141 |
+ Description: "", |
|
| 142 |
+ Value: []string{m.Network.Type},
|
|
| 143 |
+ }) |
|
| 144 |
+ } |
|
| 145 |
+ for _, mount := range m.Mounts {
|
|
| 146 |
+ if mount.Source != nil {
|
|
| 147 |
+ privileges = append(privileges, types.PluginPrivilege{
|
|
| 148 |
+ Name: "mount", |
|
| 149 |
+ Description: "", |
|
| 150 |
+ Value: []string{*mount.Source},
|
|
| 151 |
+ }) |
|
| 152 |
+ } |
|
| 153 |
+ } |
|
| 154 |
+ for _, device := range m.Devices {
|
|
| 155 |
+ if device.Path != nil {
|
|
| 156 |
+ privileges = append(privileges, types.PluginPrivilege{
|
|
| 157 |
+ Name: "device", |
|
| 158 |
+ Description: "", |
|
| 159 |
+ Value: []string{*device.Path},
|
|
| 160 |
+ }) |
|
| 161 |
+ } |
|
| 162 |
+ } |
|
| 163 |
+ if len(m.Capabilities) > 0 {
|
|
| 164 |
+ privileges = append(privileges, types.PluginPrivilege{
|
|
| 165 |
+ Name: "capabilities", |
|
| 166 |
+ Description: "", |
|
| 167 |
+ Value: m.Capabilities, |
|
| 168 |
+ }) |
|
| 169 |
+ } |
|
| 170 |
+ return privileges |
|
| 171 |
+} |
|
| 172 |
+ |
|
| 173 |
+// IsEnabled returns the active state of the plugin. |
|
| 174 |
+func (p *Plugin) IsEnabled() bool {
|
|
| 175 |
+ p.RLock() |
|
| 176 |
+ defer p.RUnlock() |
|
| 177 |
+ |
|
| 178 |
+ return p.PluginObj.Enabled |
|
| 179 |
+} |
|
| 180 |
+ |
|
| 181 |
+// GetID returns the plugin's ID. |
|
| 182 |
+func (p *Plugin) GetID() string {
|
|
| 183 |
+ p.RLock() |
|
| 184 |
+ defer p.RUnlock() |
|
| 185 |
+ |
|
| 186 |
+ return p.PluginObj.ID |
|
| 187 |
+} |
|
| 188 |
+ |
|
| 189 |
+// GetSocket returns the plugin socket. |
|
| 190 |
+func (p *Plugin) GetSocket() string {
|
|
| 191 |
+ p.RLock() |
|
| 192 |
+ defer p.RUnlock() |
|
| 193 |
+ |
|
| 194 |
+ return p.PluginObj.Manifest.Interface.Socket |
|
| 195 |
+} |
|
| 196 |
+ |
|
| 197 |
+// GetTypes returns the interface types of a plugin. |
|
| 198 |
+func (p *Plugin) GetTypes() []types.PluginInterfaceType {
|
|
| 199 |
+ p.RLock() |
|
| 200 |
+ defer p.RUnlock() |
|
| 201 |
+ |
|
| 202 |
+ return p.PluginObj.Manifest.Interface.Types |
|
| 203 |
+} |
|
| 204 |
+ |
|
| 205 |
+// InitSpec creates an OCI spec from the plugin's config. |
|
| 206 |
+func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
|
|
| 207 |
+ rootfs := filepath.Join(libRoot, p.PluginObj.ID, "rootfs") |
|
| 208 |
+ s.Root = specs.Root{
|
|
| 209 |
+ Path: rootfs, |
|
| 210 |
+ Readonly: false, // TODO: all plugins should be readonly? settable in manifest? |
|
| 211 |
+ } |
|
| 212 |
+ |
|
| 213 |
+ mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
|
|
| 214 |
+ Source: &p.RuntimeSourcePath, |
|
| 215 |
+ Destination: defaultPluginRuntimeDestination, |
|
| 216 |
+ Type: "bind", |
|
| 217 |
+ Options: []string{"rbind", "rshared"},
|
|
| 218 |
+ }) |
|
| 219 |
+ for _, mount := range mounts {
|
|
| 220 |
+ m := specs.Mount{
|
|
| 221 |
+ Destination: mount.Destination, |
|
| 222 |
+ Type: mount.Type, |
|
| 223 |
+ Options: mount.Options, |
|
| 224 |
+ } |
|
| 225 |
+ // TODO: if nil, then it's required and user didn't set it |
|
| 226 |
+ if mount.Source != nil {
|
|
| 227 |
+ m.Source = *mount.Source |
|
| 228 |
+ } |
|
| 229 |
+ if m.Source != "" && m.Type == "bind" {
|
|
| 230 |
+ fi, err := os.Lstat(filepath.Join(rootfs, string(os.PathSeparator), m.Destination)) // TODO: followsymlinks |
|
| 231 |
+ if err != nil {
|
|
| 232 |
+ return nil, err |
|
| 233 |
+ } |
|
| 234 |
+ if fi.IsDir() {
|
|
| 235 |
+ if err := os.MkdirAll(m.Source, 0700); err != nil {
|
|
| 236 |
+ return nil, err |
|
| 237 |
+ } |
|
| 238 |
+ } |
|
| 239 |
+ } |
|
| 240 |
+ s.Mounts = append(s.Mounts, m) |
|
| 241 |
+ } |
|
| 242 |
+ |
|
| 243 |
+ envs := make([]string, 1, len(p.PluginObj.Config.Env)+1) |
|
| 244 |
+ envs[0] = "PATH=" + system.DefaultPathEnv |
|
| 245 |
+ envs = append(envs, p.PluginObj.Config.Env...) |
|
| 246 |
+ |
|
| 247 |
+ args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...) |
|
| 248 |
+ cwd := p.PluginObj.Manifest.Workdir |
|
| 249 |
+ if len(cwd) == 0 {
|
|
| 250 |
+ cwd = "/" |
|
| 251 |
+ } |
|
| 252 |
+ s.Process = specs.Process{
|
|
| 253 |
+ Terminal: false, |
|
| 254 |
+ Args: args, |
|
| 255 |
+ Cwd: cwd, |
|
| 256 |
+ Env: envs, |
|
| 257 |
+ } |
|
| 258 |
+ |
|
| 259 |
+ return &s, nil |
|
| 260 |
+} |
| ... | ... |
@@ -7,7 +7,7 @@ import ( |
| 7 | 7 |
"sync" |
| 8 | 8 |
|
| 9 | 9 |
"github.com/docker/docker/pkg/locker" |
| 10 |
- "github.com/docker/docker/plugin" |
|
| 10 |
+ pluginStore "github.com/docker/docker/plugin/store" |
|
| 11 | 11 |
"github.com/docker/docker/volume" |
| 12 | 12 |
) |
| 13 | 13 |
|
| ... | ... |
@@ -102,7 +102,7 @@ func lookup(name string) (volume.Driver, error) {
|
| 102 | 102 |
return ext, nil |
| 103 | 103 |
} |
| 104 | 104 |
|
| 105 |
- p, err := plugin.LookupWithCapability(name, extName) |
|
| 105 |
+ p, err := pluginStore.LookupWithCapability(name, extName) |
|
| 106 | 106 |
if err != nil {
|
| 107 | 107 |
return nil, fmt.Errorf("Error looking up volume plugin %s: %v", name, err)
|
| 108 | 108 |
} |
| ... | ... |
@@ -151,7 +151,7 @@ func GetDriverList() []string {
|
| 151 | 151 |
|
| 152 | 152 |
// GetAllDrivers lists all the registered drivers |
| 153 | 153 |
func GetAllDrivers() ([]volume.Driver, error) {
|
| 154 |
- plugins, err := plugin.FindWithCapability(extName) |
|
| 154 |
+ plugins, err := pluginStore.FindWithCapability(extName) |
|
| 155 | 155 |
if err != nil {
|
| 156 | 156 |
return nil, fmt.Errorf("error listing plugins: %v", err)
|
| 157 | 157 |
} |