Make graphdrivers work with pluginv2.
| 6 | 4 |
old mode 100755 |
| 7 | 5 |
new mode 100644 |
| ... | ... |
@@ -47,6 +47,7 @@ import ( |
| 47 | 47 |
"github.com/docker/docker/pkg/sysinfo" |
| 48 | 48 |
"github.com/docker/docker/pkg/system" |
| 49 | 49 |
"github.com/docker/docker/pkg/truncindex" |
| 50 |
+ pluginstore "github.com/docker/docker/plugin/store" |
|
| 50 | 51 |
"github.com/docker/docker/reference" |
| 51 | 52 |
"github.com/docker/docker/registry" |
| 52 | 53 |
"github.com/docker/docker/runconfig" |
| ... | ... |
@@ -94,6 +95,7 @@ type Daemon struct {
|
| 94 | 94 |
gidMaps []idtools.IDMap |
| 95 | 95 |
layerStore layer.Store |
| 96 | 96 |
imageStore image.Store |
| 97 |
+ pluginStore *pluginstore.Store |
|
| 97 | 98 |
nameIndex *registrar.Registrar |
| 98 | 99 |
linkIndex *linkIndex |
| 99 | 100 |
containerd libcontainerd.Client |
| ... | ... |
@@ -548,6 +550,9 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot |
| 548 | 548 |
if driverName == "" {
|
| 549 | 549 |
driverName = config.GraphDriver |
| 550 | 550 |
} |
| 551 |
+ |
|
| 552 |
+ d.pluginStore = pluginstore.NewStore(config.Root) |
|
| 553 |
+ |
|
| 551 | 554 |
d.layerStore, err = layer.NewStoreFromOptions(layer.StoreOptions{
|
| 552 | 555 |
StorePath: config.Root, |
| 553 | 556 |
MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"), |
| ... | ... |
@@ -555,6 +560,7 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot |
| 555 | 555 |
GraphDriverOptions: config.GraphOptions, |
| 556 | 556 |
UIDMaps: uidMaps, |
| 557 | 557 |
GIDMaps: gidMaps, |
| 558 |
+ PluginGetter: d.pluginStore, |
|
| 558 | 559 |
}) |
| 559 | 560 |
if err != nil {
|
| 560 | 561 |
return nil, err |
| ... | ... |
@@ -911,6 +917,8 @@ func (daemon *Daemon) configureVolumes(rootUID, rootGID int) (*store.VolumeStore |
| 911 | 911 |
return nil, err |
| 912 | 912 |
} |
| 913 | 913 |
|
| 914 |
+ volumedrivers.RegisterPluginGetter(daemon.pluginStore) |
|
| 915 |
+ |
|
| 914 | 916 |
if !volumedrivers.Register(volumesDriver, volumesDriver.Name()) {
|
| 915 | 917 |
return nil, fmt.Errorf("local volume driver could not be registered")
|
| 916 | 918 |
} |
| ... | ... |
@@ -13,7 +13,7 @@ func (daemon *Daemon) verifyExperimentalContainerSettings(hostConfig *container. |
| 13 | 13 |
} |
| 14 | 14 |
|
| 15 | 15 |
func pluginInit(d *Daemon, cfg *Config, remote libcontainerd.Remote) error {
|
| 16 |
- return plugin.Init(cfg.Root, remote, d.RegistryService, cfg.LiveRestoreEnabled, d.LogPluginEvent) |
|
| 16 |
+ return plugin.Init(cfg.Root, d.pluginStore, remote, d.RegistryService, cfg.LiveRestoreEnabled, d.LogPluginEvent) |
|
| 17 | 17 |
} |
| 18 | 18 |
|
| 19 | 19 |
func pluginShutdown() {
|
| ... | ... |
@@ -12,6 +12,7 @@ import ( |
| 12 | 12 |
|
| 13 | 13 |
"github.com/docker/docker/pkg/archive" |
| 14 | 14 |
"github.com/docker/docker/pkg/idtools" |
| 15 |
+ "github.com/docker/docker/plugin/getter" |
|
| 15 | 16 |
) |
| 16 | 17 |
|
| 17 | 18 |
// FsMagic unsigned id of the filesystem in use. |
| ... | ... |
@@ -134,11 +135,11 @@ func Register(name string, initFunc InitFunc) error {
|
| 134 | 134 |
} |
| 135 | 135 |
|
| 136 | 136 |
// GetDriver initializes and returns the registered driver |
| 137 |
-func GetDriver(name, home string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error) {
|
|
| 137 |
+func GetDriver(name, home string, options []string, uidMaps, gidMaps []idtools.IDMap, plugingetter getter.PluginGetter) (Driver, error) {
|
|
| 138 | 138 |
if initFunc, exists := drivers[name]; exists {
|
| 139 | 139 |
return initFunc(filepath.Join(home, name), options, uidMaps, gidMaps) |
| 140 | 140 |
} |
| 141 |
- if pluginDriver, err := lookupPlugin(name, home, options); err == nil {
|
|
| 141 |
+ if pluginDriver, err := lookupPlugin(name, home, options, plugingetter); err == nil {
|
|
| 142 | 142 |
return pluginDriver, nil |
| 143 | 143 |
} |
| 144 | 144 |
logrus.Errorf("Failed to GetDriver graph %s %s", name, home)
|
| ... | ... |
@@ -155,10 +156,10 @@ func getBuiltinDriver(name, home string, options []string, uidMaps, gidMaps []id |
| 155 | 155 |
} |
| 156 | 156 |
|
| 157 | 157 |
// New creates the driver and initializes it at the specified root. |
| 158 |
-func New(root string, name string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error) {
|
|
| 158 |
+func New(root string, name string, options []string, uidMaps, gidMaps []idtools.IDMap, plugingetter getter.PluginGetter) (Driver, error) {
|
|
| 159 | 159 |
if name != "" {
|
| 160 | 160 |
logrus.Debugf("[graphdriver] trying provided driver: %s", name) // so the logs show specified driver
|
| 161 |
- return GetDriver(name, root, options, uidMaps, gidMaps) |
|
| 161 |
+ return GetDriver(name, root, options, uidMaps, gidMaps, plugingetter) |
|
| 162 | 162 |
} |
| 163 | 163 |
|
| 164 | 164 |
// Guess for prior driver |
| ... | ... |
@@ -41,7 +41,7 @@ func newDriver(t testing.TB, name string, options []string) *Driver {
|
| 41 | 41 |
t.Fatal(err) |
| 42 | 42 |
} |
| 43 | 43 |
|
| 44 |
- d, err := graphdriver.GetDriver(name, root, options, nil, nil) |
|
| 44 |
+ d, err := graphdriver.GetDriver(name, root, options, nil, nil, nil) |
|
| 45 | 45 |
if err != nil {
|
| 46 | 46 |
t.Logf("graphdriver: %v\n", err)
|
| 47 | 47 |
if err == graphdriver.ErrNotSupported || err == graphdriver.ErrPrerequisites || err == graphdriver.ErrIncompatibleFS {
|
| ... | ... |
@@ -6,7 +6,7 @@ import ( |
| 6 | 6 |
"fmt" |
| 7 | 7 |
"io" |
| 8 | 8 |
|
| 9 |
- "github.com/docker/docker/pkg/plugins" |
|
| 9 |
+ "github.com/docker/docker/plugin/getter" |
|
| 10 | 10 |
) |
| 11 | 11 |
|
| 12 | 12 |
type pluginClient interface {
|
| ... | ... |
@@ -18,8 +18,8 @@ type pluginClient interface {
|
| 18 | 18 |
SendFile(string, io.Reader, interface{}) error
|
| 19 | 19 |
} |
| 20 | 20 |
|
| 21 |
-func lookupPlugin(name, home string, opts []string) (Driver, error) {
|
|
| 22 |
- pl, err := plugins.Get(name, "GraphDriver") |
|
| 21 |
+func lookupPlugin(name, home string, opts []string, pluginGetter getter.PluginGetter) (Driver, error) {
|
|
| 22 |
+ pl, err := pluginGetter.Get(name, "GraphDriver", getter.LOOKUP) |
|
| 23 | 23 |
if err != nil {
|
| 24 | 24 |
return nil, fmt.Errorf("Error looking up graphdriver plugin %s: %v", name, err)
|
| 25 | 25 |
} |
| ... | ... |
@@ -2,6 +2,8 @@ |
| 2 | 2 |
|
| 3 | 3 |
package graphdriver |
| 4 | 4 |
|
| 5 |
-func lookupPlugin(name, home string, opts []string) (Driver, error) {
|
|
| 5 |
+import "github.com/docker/docker/plugin/getter" |
|
| 6 |
+ |
|
| 7 |
+func lookupPlugin(name, home string, opts []string, plugingetter getter.PluginGetter) (Driver, error) {
|
|
| 6 | 8 |
return nil, ErrNotSupported |
| 7 | 9 |
} |
| ... | ... |
@@ -14,6 +14,7 @@ import ( |
| 14 | 14 |
"github.com/docker/docker/pkg/archive" |
| 15 | 15 |
"github.com/docker/docker/pkg/idtools" |
| 16 | 16 |
"github.com/docker/docker/pkg/stringid" |
| 17 |
+ "github.com/docker/docker/plugin/getter" |
|
| 17 | 18 |
"github.com/vbatts/tar-split/tar/asm" |
| 18 | 19 |
"github.com/vbatts/tar-split/tar/storage" |
| 19 | 20 |
) |
| ... | ... |
@@ -44,6 +45,7 @@ type StoreOptions struct {
|
| 44 | 44 |
GraphDriverOptions []string |
| 45 | 45 |
UIDMaps []idtools.IDMap |
| 46 | 46 |
GIDMaps []idtools.IDMap |
| 47 |
+ PluginGetter getter.PluginGetter |
|
| 47 | 48 |
} |
| 48 | 49 |
|
| 49 | 50 |
// NewStoreFromOptions creates a new Store instance |
| ... | ... |
@@ -53,7 +55,8 @@ func NewStoreFromOptions(options StoreOptions) (Store, error) {
|
| 53 | 53 |
options.GraphDriver, |
| 54 | 54 |
options.GraphDriverOptions, |
| 55 | 55 |
options.UIDMaps, |
| 56 |
- options.GIDMaps) |
|
| 56 |
+ options.GIDMaps, |
|
| 57 |
+ options.PluginGetter) |
|
| 57 | 58 |
if err != nil {
|
| 58 | 59 |
return nil, fmt.Errorf("error initializing graphdriver: %v", err)
|
| 59 | 60 |
} |
| ... | ... |
@@ -39,7 +39,7 @@ func newVFSGraphDriver(td string) (graphdriver.Driver, error) {
|
| 39 | 39 |
}, |
| 40 | 40 |
} |
| 41 | 41 |
|
| 42 |
- return graphdriver.GetDriver("vfs", td, nil, uidMap, gidMap)
|
|
| 42 |
+ return graphdriver.GetDriver("vfs", td, nil, uidMap, gidMap, nil)
|
|
| 43 | 43 |
} |
| 44 | 44 |
|
| 45 | 45 |
func newTestGraphDriver(t *testing.T) (graphdriver.Driver, func()) {
|
| ... | ... |
@@ -83,8 +83,8 @@ func (p *Plugin) Client() *Client {
|
| 83 | 83 |
return p.client |
| 84 | 84 |
} |
| 85 | 85 |
|
| 86 |
-// IsLegacy returns true for legacy plugins and false otherwise. |
|
| 87 |
-func (p *Plugin) IsLegacy() bool {
|
|
| 86 |
+// IsV1 returns true for V1 plugins and false otherwise. |
|
| 87 |
+func (p *Plugin) IsV1() bool {
|
|
| 88 | 88 |
return true |
| 89 | 89 |
} |
| 90 | 90 |
|
| ... | ... |
@@ -17,7 +17,6 @@ import ( |
| 17 | 17 |
"github.com/docker/docker/pkg/stringid" |
| 18 | 18 |
"github.com/docker/docker/plugin/distribution" |
| 19 | 19 |
"github.com/docker/docker/plugin/v2" |
| 20 |
- "github.com/docker/docker/reference" |
|
| 21 | 20 |
) |
| 22 | 21 |
|
| 23 | 22 |
// Disable deactivates a plugin, which implies that they cannot be used by containers. |
| ... | ... |
@@ -57,9 +56,9 @@ func (pm *Manager) Inspect(name string) (tp types.Plugin, err error) {
|
| 57 | 57 |
|
| 58 | 58 |
// Pull pulls a plugin and computes the privileges required to install it. |
| 59 | 59 |
func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.AuthConfig) (types.PluginPrivileges, error) {
|
| 60 |
- ref, err := reference.ParseNamed(name) |
|
| 60 |
+ ref, err := distribution.GetRef(name) |
|
| 61 | 61 |
if err != nil {
|
| 62 |
- logrus.Debugf("error in reference.ParseNamed: %v", err)
|
|
| 62 |
+ logrus.Debugf("error in distribution.GetRef: %v", err)
|
|
| 63 | 63 |
return nil, err |
| 64 | 64 |
} |
| 65 | 65 |
name = ref.String() |
| ... | ... |
@@ -76,7 +75,7 @@ func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.A |
| 76 | 76 |
return nil, err |
| 77 | 77 |
} |
| 78 | 78 |
|
| 79 |
- pd, err := distribution.Pull(name, pm.registryService, metaHeader, authConfig) |
|
| 79 |
+ pd, err := distribution.Pull(ref, pm.registryService, metaHeader, authConfig) |
|
| 80 | 80 |
if err != nil {
|
| 81 | 81 |
logrus.Debugf("error in distribution.Pull(): %v", err)
|
| 82 | 82 |
return nil, err |
| ... | ... |
@@ -87,10 +86,7 @@ func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.A |
| 87 | 87 |
return nil, err |
| 88 | 88 |
} |
| 89 | 89 |
|
| 90 |
- var tag string |
|
| 91 |
- if ref, ok := ref.(reference.NamedTagged); ok {
|
|
| 92 |
- tag = ref.Tag() |
|
| 93 |
- } |
|
| 90 |
+ tag := distribution.GetTag(ref) |
|
| 94 | 91 |
p := v2.NewPlugin(ref.Name(), pluginID, pm.runRoot, tag) |
| 95 | 92 |
if err := p.InitPlugin(pm.libRoot); err != nil {
|
| 96 | 93 |
return nil, err |
| ... | ... |
@@ -62,14 +62,26 @@ func (pd *pullData) Layer() (io.ReadCloser, error) {
|
| 62 | 62 |
return rsc, nil |
| 63 | 63 |
} |
| 64 | 64 |
|
| 65 |
-// Pull downloads the plugin from Store |
|
| 66 |
-func Pull(name string, rs registry.Service, metaheader http.Header, authConfig *types.AuthConfig) (PullData, error) {
|
|
| 65 |
+// GetRef returns the distribution reference for a given name. |
|
| 66 |
+func GetRef(name string) (reference.Named, error) {
|
|
| 67 | 67 |
ref, err := reference.ParseNamed(name) |
| 68 | 68 |
if err != nil {
|
| 69 |
- logrus.Debugf("pull.go: error in ParseNamed: %v", err)
|
|
| 70 | 69 |
return nil, err |
| 71 | 70 |
} |
| 71 |
+ return ref, nil |
|
| 72 |
+} |
|
| 72 | 73 |
|
| 74 |
+// GetTag returns the tag associated with the given reference name. |
|
| 75 |
+func GetTag(ref reference.Named) string {
|
|
| 76 |
+ tag := DefaultTag |
|
| 77 |
+ if ref, ok := ref.(reference.NamedTagged); ok {
|
|
| 78 |
+ tag = ref.Tag() |
|
| 79 |
+ } |
|
| 80 |
+ return tag |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+// Pull downloads the plugin from Store |
|
| 84 |
+func Pull(ref reference.Named, rs registry.Service, metaheader http.Header, authConfig *types.AuthConfig) (PullData, error) {
|
|
| 73 | 85 |
repoInfo, err := rs.ResolveRepository(ref) |
| 74 | 86 |
if err != nil {
|
| 75 | 87 |
logrus.Debugf("pull.go: error in ResolveRepository: %v", err)
|
| 76 | 88 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,25 @@ |
| 0 |
+package getter |
|
| 1 |
+ |
|
| 2 |
+import "github.com/docker/docker/pkg/plugins" |
|
| 3 |
+ |
|
| 4 |
+const ( |
|
| 5 |
+ // LOOKUP doesn't update RefCount |
|
| 6 |
+ LOOKUP = 0 |
|
| 7 |
+ // CREATE increments RefCount |
|
| 8 |
+ CREATE = 1 |
|
| 9 |
+ // REMOVE decrements RefCount |
|
| 10 |
+ REMOVE = -1 |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+// CompatPlugin is a abstraction to handle both v2(new) and v1(legacy) plugins. |
|
| 14 |
+type CompatPlugin interface {
|
|
| 15 |
+ Client() *plugins.Client |
|
| 16 |
+ Name() string |
|
| 17 |
+ IsV1() bool |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+// PluginGetter is the interface implemented by Store |
|
| 21 |
+type PluginGetter interface {
|
|
| 22 |
+ Get(name, capability string, mode int) (CompatPlugin, error) |
|
| 23 |
+ GetAllByCap(capability string) ([]CompatPlugin, error) |
|
| 24 |
+} |
| ... | ... |
@@ -35,7 +35,7 @@ type Manager struct {
|
| 35 | 35 |
sync.RWMutex |
| 36 | 36 |
libRoot string |
| 37 | 37 |
runRoot string |
| 38 |
- pluginStore *store.PluginStore |
|
| 38 |
+ pluginStore *store.Store |
|
| 39 | 39 |
containerdClient libcontainerd.Client |
| 40 | 40 |
registryService registry.Service |
| 41 | 41 |
liveRestore bool |
| ... | ... |
@@ -50,7 +50,7 @@ func GetManager() *Manager {
|
| 50 | 50 |
|
| 51 | 51 |
// Init (was NewManager) instantiates the singleton Manager. |
| 52 | 52 |
// TODO: revert this to NewManager once we get rid of all the singletons. |
| 53 |
-func Init(root string, remote libcontainerd.Remote, rs registry.Service, liveRestore bool, evL eventLogger) (err error) {
|
|
| 53 |
+func Init(root string, ps *store.Store, remote libcontainerd.Remote, rs registry.Service, liveRestore bool, evL eventLogger) (err error) {
|
|
| 54 | 54 |
if manager != nil {
|
| 55 | 55 |
return nil |
| 56 | 56 |
} |
| ... | ... |
@@ -59,7 +59,7 @@ func Init(root string, remote libcontainerd.Remote, rs registry.Service, liveRes |
| 59 | 59 |
manager = &Manager{
|
| 60 | 60 |
libRoot: root, |
| 61 | 61 |
runRoot: "/run/docker", |
| 62 |
- pluginStore: store.NewPluginStore(root), |
|
| 62 |
+ pluginStore: ps, |
|
| 63 | 63 |
registryService: rs, |
| 64 | 64 |
liveRestore: liveRestore, |
| 65 | 65 |
pluginEventLogger: evL, |
| 66 | 66 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,31 @@ |
| 0 |
+package store |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "path/filepath" |
|
| 4 |
+ "sync" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/docker/docker/pkg/plugins" |
|
| 7 |
+ "github.com/docker/docker/plugin/v2" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// Store manages the plugin inventory in memory and on-disk |
|
| 11 |
+type Store struct {
|
|
| 12 |
+ sync.RWMutex |
|
| 13 |
+ plugins map[string]*v2.Plugin |
|
| 14 |
+ /* handlers are necessary for transition path of legacy plugins |
|
| 15 |
+ * to the new model. Legacy plugins use Handle() for registering an |
|
| 16 |
+ * activation callback.*/ |
|
| 17 |
+ handlers map[string]func(string, *plugins.Client) |
|
| 18 |
+ nameToID map[string]string |
|
| 19 |
+ plugindb string |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+// NewStore creates a Store. |
|
| 23 |
+func NewStore(libRoot string) *Store {
|
|
| 24 |
+ return &Store{
|
|
| 25 |
+ plugins: make(map[string]*v2.Plugin), |
|
| 26 |
+ handlers: make(map[string]func(string, *plugins.Client)), |
|
| 27 |
+ nameToID: make(map[string]string), |
|
| 28 |
+ plugindb: filepath.Join(libRoot, "plugins", "plugins.json"), |
|
| 29 |
+ } |
|
| 30 |
+} |
| 0 | 31 |
deleted file mode 100644 |
| ... | ... |
@@ -1,19 +0,0 @@ |
| 1 |
-package store |
|
| 2 |
- |
|
| 3 |
-import "github.com/docker/docker/pkg/plugins" |
|
| 4 |
- |
|
| 5 |
-const ( |
|
| 6 |
- // LOOKUP doesn't update RefCount |
|
| 7 |
- LOOKUP = 0 |
|
| 8 |
- // CREATE increments RefCount |
|
| 9 |
- CREATE = 1 |
|
| 10 |
- // REMOVE decrements RefCount |
|
| 11 |
- REMOVE = -1 |
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-// CompatPlugin is an abstraction to handle both new and legacy (v1) plugins. |
|
| 15 |
-type CompatPlugin interface {
|
|
| 16 |
- Client() *plugins.Client |
|
| 17 |
- Name() string |
|
| 18 |
- IsLegacy() bool |
|
| 19 |
-} |
| 20 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,25 +0,0 @@ |
| 1 |
-// +build !experimental |
|
| 2 |
- |
|
| 3 |
-package store |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "github.com/docker/docker/pkg/plugins" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-// FindWithCapability returns a list of plugins matching the given capability. |
|
| 10 |
-func FindWithCapability(capability string) ([]CompatPlugin, error) {
|
|
| 11 |
- pl, err := plugins.GetAll(capability) |
|
| 12 |
- if err != nil {
|
|
| 13 |
- return nil, err |
|
| 14 |
- } |
|
| 15 |
- result := make([]CompatPlugin, len(pl)) |
|
| 16 |
- for i, p := range pl {
|
|
| 17 |
- result[i] = p |
|
| 18 |
- } |
|
| 19 |
- return result, nil |
|
| 20 |
-} |
|
| 21 |
- |
|
| 22 |
-// LookupWithCapability returns a plugin matching the given name and capability. |
|
| 23 |
-func LookupWithCapability(name, capability string, _ int) (CompatPlugin, error) {
|
|
| 24 |
- return plugins.Get(name, capability) |
|
| 25 |
-} |
| ... | ... |
@@ -1,257 +1,26 @@ |
| 1 |
-// +build experimental |
|
| 1 |
+// +build !experimental |
|
| 2 | 2 |
|
| 3 | 3 |
package store |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
- "encoding/json" |
|
| 7 |
- "fmt" |
|
| 8 |
- "path/filepath" |
|
| 9 |
- "strings" |
|
| 10 |
- "sync" |
|
| 11 |
- |
|
| 12 |
- "github.com/Sirupsen/logrus" |
|
| 13 |
- "github.com/docker/docker/pkg/ioutils" |
|
| 14 | 6 |
"github.com/docker/docker/pkg/plugins" |
| 15 |
- "github.com/docker/docker/plugin/v2" |
|
| 16 |
- "github.com/docker/docker/reference" |
|
| 17 |
-) |
|
| 18 |
- |
|
| 19 |
-var ( |
|
| 20 |
- store *PluginStore |
|
| 21 |
- /* allowV1PluginsFallback determines daemon's support for V1 plugins. |
|
| 22 |
- * When the time comes to remove support for V1 plugins, flipping |
|
| 23 |
- * this bool is all that will be needed. |
|
| 24 |
- */ |
|
| 25 |
- allowV1PluginsFallback = true |
|
| 7 |
+ "github.com/docker/docker/plugin/getter" |
|
| 26 | 8 |
) |
| 27 | 9 |
|
| 28 |
-// ErrNotFound indicates that a plugin was not found locally. |
|
| 29 |
-type ErrNotFound string |
|
| 30 |
- |
|
| 31 |
-func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }
|
|
| 32 |
- |
|
| 33 |
-// PluginStore manages the plugin inventory in memory and on-disk |
|
| 34 |
-type PluginStore struct {
|
|
| 35 |
- sync.RWMutex |
|
| 36 |
- plugins map[string]*v2.Plugin |
|
| 37 |
- /* handlers are necessary for transition path of legacy plugins |
|
| 38 |
- * to the new model. Legacy plugins use Handle() for registering an |
|
| 39 |
- * activation callback.*/ |
|
| 40 |
- handlers map[string]func(string, *plugins.Client) |
|
| 41 |
- nameToID map[string]string |
|
| 42 |
- plugindb string |
|
| 43 |
-} |
|
| 44 |
- |
|
| 45 |
-// NewPluginStore creates a PluginStore. |
|
| 46 |
-func NewPluginStore(libRoot string) *PluginStore {
|
|
| 47 |
- store = &PluginStore{
|
|
| 48 |
- plugins: make(map[string]*v2.Plugin), |
|
| 49 |
- handlers: make(map[string]func(string, *plugins.Client)), |
|
| 50 |
- nameToID: make(map[string]string), |
|
| 51 |
- plugindb: filepath.Join(libRoot, "plugins.json"), |
|
| 52 |
- } |
|
| 53 |
- return store |
|
| 54 |
-} |
|
| 55 |
- |
|
| 56 |
-// GetByName retreives a plugin by name. |
|
| 57 |
-func (ps *PluginStore) GetByName(name string) (*v2.Plugin, error) {
|
|
| 58 |
- ps.RLock() |
|
| 59 |
- defer ps.RUnlock() |
|
| 60 |
- |
|
| 61 |
- id, nameOk := ps.nameToID[name] |
|
| 62 |
- if !nameOk {
|
|
| 63 |
- return nil, ErrNotFound(name) |
|
| 64 |
- } |
|
| 65 |
- |
|
| 66 |
- p, idOk := ps.plugins[id] |
|
| 67 |
- if !idOk {
|
|
| 68 |
- return nil, ErrNotFound(id) |
|
| 69 |
- } |
|
| 70 |
- return p, nil |
|
| 71 |
-} |
|
| 72 |
- |
|
| 73 |
-// GetByID retreives a plugin by ID. |
|
| 74 |
-func (ps *PluginStore) GetByID(id string) (*v2.Plugin, error) {
|
|
| 75 |
- ps.RLock() |
|
| 76 |
- defer ps.RUnlock() |
|
| 77 |
- |
|
| 78 |
- p, idOk := ps.plugins[id] |
|
| 79 |
- if !idOk {
|
|
| 80 |
- return nil, ErrNotFound(id) |
|
| 81 |
- } |
|
| 82 |
- return p, nil |
|
| 83 |
-} |
|
| 84 |
- |
|
| 85 |
-// GetAll retreives all plugins. |
|
| 86 |
-func (ps *PluginStore) GetAll() map[string]*v2.Plugin {
|
|
| 87 |
- ps.RLock() |
|
| 88 |
- defer ps.RUnlock() |
|
| 89 |
- return ps.plugins |
|
| 90 |
-} |
|
| 91 |
- |
|
| 92 |
-// SetAll initialized plugins during daemon restore. |
|
| 93 |
-func (ps *PluginStore) SetAll(plugins map[string]*v2.Plugin) {
|
|
| 94 |
- ps.Lock() |
|
| 95 |
- defer ps.Unlock() |
|
| 96 |
- ps.plugins = plugins |
|
| 97 |
-} |
|
| 98 |
- |
|
| 99 |
-func (ps *PluginStore) getByCap(name string, capability string) (*v2.Plugin, error) {
|
|
| 100 |
- ps.RLock() |
|
| 101 |
- defer ps.RUnlock() |
|
| 102 |
- |
|
| 103 |
- p, err := ps.GetByName(name) |
|
| 10 |
+// GetAllByCap returns a list of plugins matching the given capability. |
|
| 11 |
+func (ps Store) GetAllByCap(capability string) ([]getter.CompatPlugin, error) {
|
|
| 12 |
+ pl, err := plugins.GetAll(capability) |
|
| 104 | 13 |
if err != nil {
|
| 105 | 14 |
return nil, err |
| 106 | 15 |
} |
| 107 |
- return p.FilterByCap(capability) |
|
| 108 |
-} |
|
| 109 |
- |
|
| 110 |
-func (ps *PluginStore) getAllByCap(capability string) []CompatPlugin {
|
|
| 111 |
- ps.RLock() |
|
| 112 |
- defer ps.RUnlock() |
|
| 113 |
- |
|
| 114 |
- result := make([]CompatPlugin, 0, 1) |
|
| 115 |
- for _, p := range ps.plugins {
|
|
| 116 |
- if _, err := p.FilterByCap(capability); err == nil {
|
|
| 117 |
- result = append(result, p) |
|
| 118 |
- } |
|
| 119 |
- } |
|
| 120 |
- return result |
|
| 121 |
-} |
|
| 122 |
- |
|
| 123 |
-// SetState sets the active state of the plugin and updates plugindb. |
|
| 124 |
-func (ps *PluginStore) SetState(p *v2.Plugin, state bool) {
|
|
| 125 |
- ps.Lock() |
|
| 126 |
- defer ps.Unlock() |
|
| 127 |
- |
|
| 128 |
- p.PluginObj.Enabled = state |
|
| 129 |
- ps.updatePluginDB() |
|
| 130 |
-} |
|
| 131 |
- |
|
| 132 |
-// Add adds a plugin to memory and plugindb. |
|
| 133 |
-func (ps *PluginStore) Add(p *v2.Plugin) {
|
|
| 134 |
- ps.Lock() |
|
| 135 |
- ps.plugins[p.GetID()] = p |
|
| 136 |
- ps.nameToID[p.Name()] = p.GetID() |
|
| 137 |
- ps.updatePluginDB() |
|
| 138 |
- ps.Unlock() |
|
| 139 |
-} |
|
| 140 |
- |
|
| 141 |
-// Remove removes a plugin from memory, plugindb and disk. |
|
| 142 |
-func (ps *PluginStore) Remove(p *v2.Plugin) {
|
|
| 143 |
- ps.Lock() |
|
| 144 |
- delete(ps.plugins, p.GetID()) |
|
| 145 |
- delete(ps.nameToID, p.Name()) |
|
| 146 |
- ps.updatePluginDB() |
|
| 147 |
- p.RemoveFromDisk() |
|
| 148 |
- ps.Unlock() |
|
| 149 |
-} |
|
| 150 |
- |
|
| 151 |
-// Callers are expected to hold the store lock. |
|
| 152 |
-func (ps *PluginStore) updatePluginDB() error {
|
|
| 153 |
- jsonData, err := json.Marshal(ps.plugins) |
|
| 154 |
- if err != nil {
|
|
| 155 |
- logrus.Debugf("Error in json.Marshal: %v", err)
|
|
| 156 |
- return err |
|
| 157 |
- } |
|
| 158 |
- ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600) |
|
| 159 |
- return nil |
|
| 160 |
-} |
|
| 161 |
- |
|
| 162 |
-// LookupWithCapability returns a plugin matching the given name and capability. |
|
| 163 |
-func LookupWithCapability(name, capability string, mode int) (CompatPlugin, error) {
|
|
| 164 |
- var ( |
|
| 165 |
- p *v2.Plugin |
|
| 166 |
- err error |
|
| 167 |
- ) |
|
| 168 |
- |
|
| 169 |
- // Lookup using new model. |
|
| 170 |
- if store != nil {
|
|
| 171 |
- fullName := name |
|
| 172 |
- if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
|
|
| 173 |
- if reference.IsNameOnly(named) {
|
|
| 174 |
- named = reference.WithDefaultTag(named) |
|
| 175 |
- } |
|
| 176 |
- ref, ok := named.(reference.NamedTagged) |
|
| 177 |
- if !ok {
|
|
| 178 |
- return nil, fmt.Errorf("invalid name: %s", named.String())
|
|
| 179 |
- } |
|
| 180 |
- fullName = ref.String() |
|
| 181 |
- } |
|
| 182 |
- p, err = store.GetByName(fullName) |
|
| 183 |
- if err == nil {
|
|
| 184 |
- p.Lock() |
|
| 185 |
- p.RefCount += mode |
|
| 186 |
- p.Unlock() |
|
| 187 |
- return p.FilterByCap(capability) |
|
| 188 |
- } |
|
| 189 |
- if _, ok := err.(ErrNotFound); !ok {
|
|
| 190 |
- return nil, err |
|
| 191 |
- } |
|
| 192 |
- } |
|
| 193 |
- |
|
| 194 |
- // Lookup using legacy model. |
|
| 195 |
- if allowV1PluginsFallback {
|
|
| 196 |
- p, err := plugins.Get(name, capability) |
|
| 197 |
- if err != nil {
|
|
| 198 |
- return nil, fmt.Errorf("legacy plugin: %v", err)
|
|
| 199 |
- } |
|
| 200 |
- return p, nil |
|
| 201 |
- } |
|
| 202 |
- |
|
| 203 |
- return nil, err |
|
| 204 |
-} |
|
| 205 |
- |
|
| 206 |
-// FindWithCapability returns a list of plugins matching the given capability. |
|
| 207 |
-func FindWithCapability(capability string) ([]CompatPlugin, error) {
|
|
| 208 |
- result := make([]CompatPlugin, 0, 1) |
|
| 209 |
- |
|
| 210 |
- /* Daemon start always calls plugin.Init thereby initializing a store. |
|
| 211 |
- * So store on experimental builds can never be nil, even while |
|
| 212 |
- * handling legacy plugins. However, there are legacy plugin unit |
|
| 213 |
- * tests where the volume subsystem directly talks with the plugin, |
|
| 214 |
- * bypassing the daemon. For such tests, this check is necessary. |
|
| 215 |
- */ |
|
| 216 |
- if store != nil {
|
|
| 217 |
- store.RLock() |
|
| 218 |
- result = store.getAllByCap(capability) |
|
| 219 |
- store.RUnlock() |
|
| 220 |
- } |
|
| 221 |
- |
|
| 222 |
- // Lookup with legacy model |
|
| 223 |
- if allowV1PluginsFallback {
|
|
| 224 |
- pl, err := plugins.GetAll(capability) |
|
| 225 |
- if err != nil {
|
|
| 226 |
- return nil, fmt.Errorf("legacy plugin: %v", err)
|
|
| 227 |
- } |
|
| 228 |
- for _, p := range pl {
|
|
| 229 |
- result = append(result, p) |
|
| 230 |
- } |
|
| 16 |
+ result := make([]getter.CompatPlugin, len(pl)) |
|
| 17 |
+ for i, p := range pl {
|
|
| 18 |
+ result[i] = p |
|
| 231 | 19 |
} |
| 232 | 20 |
return result, nil |
| 233 | 21 |
} |
| 234 | 22 |
|
| 235 |
-// Handle sets a callback for a given capability. It is only used by network |
|
| 236 |
-// and ipam drivers during plugin registration. The callback registers the |
|
| 237 |
-// driver with the subsystem (network, ipam). |
|
| 238 |
-func Handle(capability string, callback func(string, *plugins.Client)) {
|
|
| 239 |
- pluginType := fmt.Sprintf("docker.%s/1", strings.ToLower(capability))
|
|
| 240 |
- |
|
| 241 |
- // Register callback with new plugin model. |
|
| 242 |
- store.handlers[pluginType] = callback |
|
| 243 |
- |
|
| 244 |
- // Register callback with legacy plugin model. |
|
| 245 |
- if allowV1PluginsFallback {
|
|
| 246 |
- plugins.Handle(capability, callback) |
|
| 247 |
- } |
|
| 248 |
-} |
|
| 249 |
- |
|
| 250 |
-// CallHandler calls the registered callback. It is invoked during plugin enable. |
|
| 251 |
-func (ps *PluginStore) CallHandler(p *v2.Plugin) {
|
|
| 252 |
- for _, typ := range p.GetTypes() {
|
|
| 253 |
- if handler := ps.handlers[typ.String()]; handler != nil {
|
|
| 254 |
- handler(p.Name(), p.Client()) |
|
| 255 |
- } |
|
| 256 |
- } |
|
| 23 |
+// Get returns a plugin matching the given name and capability. |
|
| 24 |
+func (ps Store) Get(name, capability string, _ int) (getter.CompatPlugin, error) {
|
|
| 25 |
+ return plugins.Get(name, capability) |
|
| 257 | 26 |
} |
| 258 | 27 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,234 @@ |
| 0 |
+// +build experimental |
|
| 1 |
+ |
|
| 2 |
+package store |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "encoding/json" |
|
| 6 |
+ "fmt" |
|
| 7 |
+ "strings" |
|
| 8 |
+ |
|
| 9 |
+ "github.com/Sirupsen/logrus" |
|
| 10 |
+ "github.com/docker/docker/pkg/ioutils" |
|
| 11 |
+ "github.com/docker/docker/pkg/plugins" |
|
| 12 |
+ "github.com/docker/docker/plugin/getter" |
|
| 13 |
+ "github.com/docker/docker/plugin/v2" |
|
| 14 |
+ "github.com/docker/docker/reference" |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+/* allowV1PluginsFallback determines daemon's support for V1 plugins. |
|
| 18 |
+ * When the time comes to remove support for V1 plugins, flipping |
|
| 19 |
+ * this bool is all that will be needed. |
|
| 20 |
+ */ |
|
| 21 |
+var allowV1PluginsFallback = true |
|
| 22 |
+ |
|
| 23 |
+// ErrNotFound indicates that a plugin was not found locally. |
|
| 24 |
+type ErrNotFound string |
|
| 25 |
+ |
|
| 26 |
+func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }
|
|
| 27 |
+ |
|
| 28 |
+// GetByName retreives a plugin by name. |
|
| 29 |
+func (ps *Store) GetByName(name string) (*v2.Plugin, error) {
|
|
| 30 |
+ ps.RLock() |
|
| 31 |
+ defer ps.RUnlock() |
|
| 32 |
+ |
|
| 33 |
+ id, nameOk := ps.nameToID[name] |
|
| 34 |
+ if !nameOk {
|
|
| 35 |
+ return nil, ErrNotFound(name) |
|
| 36 |
+ } |
|
| 37 |
+ |
|
| 38 |
+ p, idOk := ps.plugins[id] |
|
| 39 |
+ if !idOk {
|
|
| 40 |
+ return nil, ErrNotFound(id) |
|
| 41 |
+ } |
|
| 42 |
+ return p, nil |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+// GetByID retreives a plugin by ID. |
|
| 46 |
+func (ps *Store) GetByID(id string) (*v2.Plugin, error) {
|
|
| 47 |
+ ps.RLock() |
|
| 48 |
+ defer ps.RUnlock() |
|
| 49 |
+ |
|
| 50 |
+ p, idOk := ps.plugins[id] |
|
| 51 |
+ if !idOk {
|
|
| 52 |
+ return nil, ErrNotFound(id) |
|
| 53 |
+ } |
|
| 54 |
+ return p, nil |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+// GetAll retreives all plugins. |
|
| 58 |
+func (ps *Store) GetAll() map[string]*v2.Plugin {
|
|
| 59 |
+ ps.RLock() |
|
| 60 |
+ defer ps.RUnlock() |
|
| 61 |
+ return ps.plugins |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 64 |
+// SetAll initialized plugins during daemon restore. |
|
| 65 |
+func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
|
|
| 66 |
+ ps.Lock() |
|
| 67 |
+ defer ps.Unlock() |
|
| 68 |
+ ps.plugins = plugins |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+func (ps *Store) getByCap(name string, capability string) (*v2.Plugin, error) {
|
|
| 72 |
+ ps.RLock() |
|
| 73 |
+ defer ps.RUnlock() |
|
| 74 |
+ |
|
| 75 |
+ p, err := ps.GetByName(name) |
|
| 76 |
+ if err != nil {
|
|
| 77 |
+ return nil, err |
|
| 78 |
+ } |
|
| 79 |
+ return p.FilterByCap(capability) |
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+func (ps *Store) getAllByCap(capability string) []getter.CompatPlugin {
|
|
| 83 |
+ ps.RLock() |
|
| 84 |
+ defer ps.RUnlock() |
|
| 85 |
+ |
|
| 86 |
+ result := make([]getter.CompatPlugin, 0, 1) |
|
| 87 |
+ for _, p := range ps.plugins {
|
|
| 88 |
+ if _, err := p.FilterByCap(capability); err == nil {
|
|
| 89 |
+ result = append(result, p) |
|
| 90 |
+ } |
|
| 91 |
+ } |
|
| 92 |
+ return result |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+// SetState sets the active state of the plugin and updates plugindb. |
|
| 96 |
+func (ps *Store) SetState(p *v2.Plugin, state bool) {
|
|
| 97 |
+ ps.Lock() |
|
| 98 |
+ defer ps.Unlock() |
|
| 99 |
+ |
|
| 100 |
+ p.PluginObj.Enabled = state |
|
| 101 |
+ ps.updatePluginDB() |
|
| 102 |
+} |
|
| 103 |
+ |
|
| 104 |
+// Add adds a plugin to memory and plugindb. |
|
| 105 |
+func (ps *Store) Add(p *v2.Plugin) {
|
|
| 106 |
+ ps.Lock() |
|
| 107 |
+ ps.plugins[p.GetID()] = p |
|
| 108 |
+ ps.nameToID[p.Name()] = p.GetID() |
|
| 109 |
+ ps.updatePluginDB() |
|
| 110 |
+ ps.Unlock() |
|
| 111 |
+} |
|
| 112 |
+ |
|
| 113 |
+// Remove removes a plugin from memory, plugindb and disk. |
|
| 114 |
+func (ps *Store) Remove(p *v2.Plugin) {
|
|
| 115 |
+ ps.Lock() |
|
| 116 |
+ delete(ps.plugins, p.GetID()) |
|
| 117 |
+ delete(ps.nameToID, p.Name()) |
|
| 118 |
+ ps.updatePluginDB() |
|
| 119 |
+ p.RemoveFromDisk() |
|
| 120 |
+ ps.Unlock() |
|
| 121 |
+} |
|
| 122 |
+ |
|
| 123 |
+// Callers are expected to hold the store lock. |
|
| 124 |
+func (ps *Store) updatePluginDB() error {
|
|
| 125 |
+ jsonData, err := json.Marshal(ps.plugins) |
|
| 126 |
+ if err != nil {
|
|
| 127 |
+ logrus.Debugf("Error in json.Marshal: %v", err)
|
|
| 128 |
+ return err |
|
| 129 |
+ } |
|
| 130 |
+ ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600) |
|
| 131 |
+ return nil |
|
| 132 |
+} |
|
| 133 |
+ |
|
| 134 |
+// Get returns a plugin matching the given name and capability. |
|
| 135 |
+func (ps *Store) Get(name, capability string, mode int) (getter.CompatPlugin, error) {
|
|
| 136 |
+ var ( |
|
| 137 |
+ p *v2.Plugin |
|
| 138 |
+ err error |
|
| 139 |
+ ) |
|
| 140 |
+ |
|
| 141 |
+ // Lookup using new model. |
|
| 142 |
+ if ps != nil {
|
|
| 143 |
+ fullName := name |
|
| 144 |
+ if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
|
|
| 145 |
+ if reference.IsNameOnly(named) {
|
|
| 146 |
+ named = reference.WithDefaultTag(named) |
|
| 147 |
+ } |
|
| 148 |
+ ref, ok := named.(reference.NamedTagged) |
|
| 149 |
+ if !ok {
|
|
| 150 |
+ return nil, fmt.Errorf("invalid name: %s", named.String())
|
|
| 151 |
+ } |
|
| 152 |
+ fullName = ref.String() |
|
| 153 |
+ } |
|
| 154 |
+ p, err = ps.GetByName(fullName) |
|
| 155 |
+ if err == nil {
|
|
| 156 |
+ p.Lock() |
|
| 157 |
+ p.RefCount += mode |
|
| 158 |
+ p.Unlock() |
|
| 159 |
+ return p.FilterByCap(capability) |
|
| 160 |
+ } |
|
| 161 |
+ if _, ok := err.(ErrNotFound); !ok {
|
|
| 162 |
+ return nil, err |
|
| 163 |
+ } |
|
| 164 |
+ } |
|
| 165 |
+ |
|
| 166 |
+ // Lookup using legacy model. |
|
| 167 |
+ if allowV1PluginsFallback {
|
|
| 168 |
+ p, err := plugins.Get(name, capability) |
|
| 169 |
+ if err != nil {
|
|
| 170 |
+ return nil, fmt.Errorf("legacy plugin: %v", err)
|
|
| 171 |
+ } |
|
| 172 |
+ return p, nil |
|
| 173 |
+ } |
|
| 174 |
+ |
|
| 175 |
+ return nil, err |
|
| 176 |
+} |
|
| 177 |
+ |
|
| 178 |
+// GetAllByCap returns a list of plugins matching the given capability. |
|
| 179 |
+func (ps *Store) GetAllByCap(capability string) ([]getter.CompatPlugin, error) {
|
|
| 180 |
+ result := make([]getter.CompatPlugin, 0, 1) |
|
| 181 |
+ |
|
| 182 |
+ /* Daemon start always calls plugin.Init thereby initializing a store. |
|
| 183 |
+ * So store on experimental builds can never be nil, even while |
|
| 184 |
+ * handling legacy plugins. However, there are legacy plugin unit |
|
| 185 |
+ * tests where the volume subsystem directly talks with the plugin, |
|
| 186 |
+ * bypassing the daemon. For such tests, this check is necessary. |
|
| 187 |
+ */ |
|
| 188 |
+ if ps != nil {
|
|
| 189 |
+ ps.RLock() |
|
| 190 |
+ result = ps.getAllByCap(capability) |
|
| 191 |
+ ps.RUnlock() |
|
| 192 |
+ } |
|
| 193 |
+ |
|
| 194 |
+ // Lookup with legacy model |
|
| 195 |
+ if allowV1PluginsFallback {
|
|
| 196 |
+ pl, err := plugins.GetAll(capability) |
|
| 197 |
+ if err != nil {
|
|
| 198 |
+ return nil, fmt.Errorf("legacy plugin: %v", err)
|
|
| 199 |
+ } |
|
| 200 |
+ for _, p := range pl {
|
|
| 201 |
+ result = append(result, p) |
|
| 202 |
+ } |
|
| 203 |
+ } |
|
| 204 |
+ return result, nil |
|
| 205 |
+} |
|
| 206 |
+ |
|
| 207 |
+// Handle sets a callback for a given capability. It is only used by network |
|
| 208 |
+// and ipam drivers during plugin registration. The callback registers the |
|
| 209 |
+// driver with the subsystem (network, ipam). |
|
| 210 |
+func (ps Store) Handle(capability string, callback func(string, *plugins.Client)) {
|
|
| 211 |
+ pluginType := fmt.Sprintf("docker.%s/1", strings.ToLower(capability))
|
|
| 212 |
+ |
|
| 213 |
+ store := &ps |
|
| 214 |
+ |
|
| 215 |
+ // Register callback with new plugin model. |
|
| 216 |
+ store.Lock() |
|
| 217 |
+ store.handlers[pluginType] = callback |
|
| 218 |
+ store.Unlock() |
|
| 219 |
+ |
|
| 220 |
+ // Register callback with legacy plugin model. |
|
| 221 |
+ if allowV1PluginsFallback {
|
|
| 222 |
+ plugins.Handle(capability, callback) |
|
| 223 |
+ } |
|
| 224 |
+} |
|
| 225 |
+ |
|
| 226 |
+// CallHandler calls the registered callback. It is invoked during plugin enable. |
|
| 227 |
+func (ps *Store) CallHandler(p *v2.Plugin) {
|
|
| 228 |
+ for _, typ := range p.GetTypes() {
|
|
| 229 |
+ if handler := ps.handlers[typ.String()]; handler != nil {
|
|
| 230 |
+ handler(p.Name(), p.Client()) |
|
| 231 |
+ } |
|
| 232 |
+ } |
|
| 233 |
+} |
| ... | ... |
@@ -1,32 +1,13 @@ |
| 1 |
-// +build experimental |
|
| 2 |
- |
|
| 3 | 1 |
package v2 |
| 4 | 2 |
|
| 5 | 3 |
import ( |
| 6 |
- "encoding/json" |
|
| 7 |
- "errors" |
|
| 8 |
- "fmt" |
|
| 9 |
- "os" |
|
| 10 |
- "path/filepath" |
|
| 11 |
- "strings" |
|
| 12 | 4 |
"sync" |
| 13 | 5 |
|
| 14 | 6 |
"github.com/docker/docker/api/types" |
| 15 | 7 |
"github.com/docker/docker/pkg/plugins" |
| 16 |
- "github.com/docker/docker/pkg/system" |
|
| 17 | 8 |
"github.com/docker/docker/restartmanager" |
| 18 |
- "github.com/opencontainers/runtime-spec/specs-go" |
|
| 19 | 9 |
) |
| 20 | 10 |
|
| 21 |
-const defaultPluginRuntimeDestination = "/run/docker/plugins" |
|
| 22 |
- |
|
| 23 |
-// ErrInadequateCapability indicates that the plugin did not have the requested capability. |
|
| 24 |
-type ErrInadequateCapability string |
|
| 25 |
- |
|
| 26 |
-func (cap ErrInadequateCapability) Error() string {
|
|
| 27 |
- return fmt.Sprintf("plugin does not provide %q capability", cap)
|
|
| 28 |
-} |
|
| 29 |
- |
|
| 30 | 11 |
// Plugin represents an individual plugin. |
| 31 | 12 |
type Plugin struct {
|
| 32 | 13 |
sync.RWMutex |
| ... | ... |
@@ -37,226 +18,3 @@ type Plugin struct {
|
| 37 | 37 |
ExitChan chan bool `json:"-"` |
| 38 | 38 |
RefCount int `json:"-"` |
| 39 | 39 |
} |
| 40 |
- |
|
| 41 |
-func newPluginObj(name, id, tag string) types.Plugin {
|
|
| 42 |
- return types.Plugin{Name: name, ID: id, Tag: tag}
|
|
| 43 |
-} |
|
| 44 |
- |
|
| 45 |
-// NewPlugin creates a plugin. |
|
| 46 |
-func NewPlugin(name, id, runRoot, tag string) *Plugin {
|
|
| 47 |
- return &Plugin{
|
|
| 48 |
- PluginObj: newPluginObj(name, id, tag), |
|
| 49 |
- RuntimeSourcePath: filepath.Join(runRoot, id), |
|
| 50 |
- } |
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 |
-// Client returns the plugin client. |
|
| 54 |
-func (p *Plugin) Client() *plugins.Client {
|
|
| 55 |
- return p.PClient |
|
| 56 |
-} |
|
| 57 |
- |
|
| 58 |
-// IsLegacy returns true for legacy plugins and false otherwise. |
|
| 59 |
-func (p *Plugin) IsLegacy() bool {
|
|
| 60 |
- return false |
|
| 61 |
-} |
|
| 62 |
- |
|
| 63 |
-// Name returns the plugin name. |
|
| 64 |
-func (p *Plugin) Name() string {
|
|
| 65 |
- name := p.PluginObj.Name |
|
| 66 |
- if len(p.PluginObj.Tag) > 0 {
|
|
| 67 |
- // TODO: this feels hacky, maybe we should be storing the distribution reference rather than splitting these |
|
| 68 |
- name += ":" + p.PluginObj.Tag |
|
| 69 |
- } |
|
| 70 |
- return name |
|
| 71 |
-} |
|
| 72 |
- |
|
| 73 |
-// FilterByCap query the plugin for a given capability. |
|
| 74 |
-func (p *Plugin) FilterByCap(capability string) (*Plugin, error) {
|
|
| 75 |
- capability = strings.ToLower(capability) |
|
| 76 |
- for _, typ := range p.PluginObj.Manifest.Interface.Types {
|
|
| 77 |
- if typ.Capability == capability && typ.Prefix == "docker" {
|
|
| 78 |
- return p, nil |
|
| 79 |
- } |
|
| 80 |
- } |
|
| 81 |
- return nil, ErrInadequateCapability(capability) |
|
| 82 |
-} |
|
| 83 |
- |
|
| 84 |
-// RemoveFromDisk deletes the plugin's runtime files from disk. |
|
| 85 |
-func (p *Plugin) RemoveFromDisk() error {
|
|
| 86 |
- return os.RemoveAll(p.RuntimeSourcePath) |
|
| 87 |
-} |
|
| 88 |
- |
|
| 89 |
-// InitPlugin populates the plugin object from the plugin manifest file. |
|
| 90 |
-func (p *Plugin) InitPlugin(libRoot string) error {
|
|
| 91 |
- dt, err := os.Open(filepath.Join(libRoot, p.PluginObj.ID, "manifest.json")) |
|
| 92 |
- if err != nil {
|
|
| 93 |
- return err |
|
| 94 |
- } |
|
| 95 |
- err = json.NewDecoder(dt).Decode(&p.PluginObj.Manifest) |
|
| 96 |
- dt.Close() |
|
| 97 |
- if err != nil {
|
|
| 98 |
- return err |
|
| 99 |
- } |
|
| 100 |
- |
|
| 101 |
- p.PluginObj.Config.Mounts = make([]types.PluginMount, len(p.PluginObj.Manifest.Mounts)) |
|
| 102 |
- for i, mount := range p.PluginObj.Manifest.Mounts {
|
|
| 103 |
- p.PluginObj.Config.Mounts[i] = mount |
|
| 104 |
- } |
|
| 105 |
- p.PluginObj.Config.Env = make([]string, 0, len(p.PluginObj.Manifest.Env)) |
|
| 106 |
- for _, env := range p.PluginObj.Manifest.Env {
|
|
| 107 |
- if env.Value != nil {
|
|
| 108 |
- p.PluginObj.Config.Env = append(p.PluginObj.Config.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
|
|
| 109 |
- } |
|
| 110 |
- } |
|
| 111 |
- copy(p.PluginObj.Config.Args, p.PluginObj.Manifest.Args.Value) |
|
| 112 |
- |
|
| 113 |
- f, err := os.Create(filepath.Join(libRoot, p.PluginObj.ID, "plugin-config.json")) |
|
| 114 |
- if err != nil {
|
|
| 115 |
- return err |
|
| 116 |
- } |
|
| 117 |
- err = json.NewEncoder(f).Encode(&p.PluginObj.Config) |
|
| 118 |
- f.Close() |
|
| 119 |
- return err |
|
| 120 |
-} |
|
| 121 |
- |
|
| 122 |
-// Set is used to pass arguments to the plugin. |
|
| 123 |
-func (p *Plugin) Set(args []string) error {
|
|
| 124 |
- m := make(map[string]string, len(args)) |
|
| 125 |
- for _, arg := range args {
|
|
| 126 |
- i := strings.Index(arg, "=") |
|
| 127 |
- if i < 0 {
|
|
| 128 |
- return fmt.Errorf("No equal sign '=' found in %s", arg)
|
|
| 129 |
- } |
|
| 130 |
- m[arg[:i]] = arg[i+1:] |
|
| 131 |
- } |
|
| 132 |
- return errors.New("not implemented")
|
|
| 133 |
-} |
|
| 134 |
- |
|
| 135 |
-// ComputePrivileges takes the manifest file and computes the list of access necessary |
|
| 136 |
-// for the plugin on the host. |
|
| 137 |
-func (p *Plugin) ComputePrivileges() types.PluginPrivileges {
|
|
| 138 |
- m := p.PluginObj.Manifest |
|
| 139 |
- var privileges types.PluginPrivileges |
|
| 140 |
- if m.Network.Type != "null" && m.Network.Type != "bridge" {
|
|
| 141 |
- privileges = append(privileges, types.PluginPrivilege{
|
|
| 142 |
- Name: "network", |
|
| 143 |
- Description: "", |
|
| 144 |
- Value: []string{m.Network.Type},
|
|
| 145 |
- }) |
|
| 146 |
- } |
|
| 147 |
- for _, mount := range m.Mounts {
|
|
| 148 |
- if mount.Source != nil {
|
|
| 149 |
- privileges = append(privileges, types.PluginPrivilege{
|
|
| 150 |
- Name: "mount", |
|
| 151 |
- Description: "", |
|
| 152 |
- Value: []string{*mount.Source},
|
|
| 153 |
- }) |
|
| 154 |
- } |
|
| 155 |
- } |
|
| 156 |
- for _, device := range m.Devices {
|
|
| 157 |
- if device.Path != nil {
|
|
| 158 |
- privileges = append(privileges, types.PluginPrivilege{
|
|
| 159 |
- Name: "device", |
|
| 160 |
- Description: "", |
|
| 161 |
- Value: []string{*device.Path},
|
|
| 162 |
- }) |
|
| 163 |
- } |
|
| 164 |
- } |
|
| 165 |
- if len(m.Capabilities) > 0 {
|
|
| 166 |
- privileges = append(privileges, types.PluginPrivilege{
|
|
| 167 |
- Name: "capabilities", |
|
| 168 |
- Description: "", |
|
| 169 |
- Value: m.Capabilities, |
|
| 170 |
- }) |
|
| 171 |
- } |
|
| 172 |
- return privileges |
|
| 173 |
-} |
|
| 174 |
- |
|
| 175 |
-// IsEnabled returns the active state of the plugin. |
|
| 176 |
-func (p *Plugin) IsEnabled() bool {
|
|
| 177 |
- p.RLock() |
|
| 178 |
- defer p.RUnlock() |
|
| 179 |
- |
|
| 180 |
- return p.PluginObj.Enabled |
|
| 181 |
-} |
|
| 182 |
- |
|
| 183 |
-// GetID returns the plugin's ID. |
|
| 184 |
-func (p *Plugin) GetID() string {
|
|
| 185 |
- p.RLock() |
|
| 186 |
- defer p.RUnlock() |
|
| 187 |
- |
|
| 188 |
- return p.PluginObj.ID |
|
| 189 |
-} |
|
| 190 |
- |
|
| 191 |
-// GetSocket returns the plugin socket. |
|
| 192 |
-func (p *Plugin) GetSocket() string {
|
|
| 193 |
- p.RLock() |
|
| 194 |
- defer p.RUnlock() |
|
| 195 |
- |
|
| 196 |
- return p.PluginObj.Manifest.Interface.Socket |
|
| 197 |
-} |
|
| 198 |
- |
|
| 199 |
-// GetTypes returns the interface types of a plugin. |
|
| 200 |
-func (p *Plugin) GetTypes() []types.PluginInterfaceType {
|
|
| 201 |
- p.RLock() |
|
| 202 |
- defer p.RUnlock() |
|
| 203 |
- |
|
| 204 |
- return p.PluginObj.Manifest.Interface.Types |
|
| 205 |
-} |
|
| 206 |
- |
|
| 207 |
-// InitSpec creates an OCI spec from the plugin's config. |
|
| 208 |
-func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
|
|
| 209 |
- rootfs := filepath.Join(libRoot, p.PluginObj.ID, "rootfs") |
|
| 210 |
- s.Root = specs.Root{
|
|
| 211 |
- Path: rootfs, |
|
| 212 |
- Readonly: false, // TODO: all plugins should be readonly? settable in manifest? |
|
| 213 |
- } |
|
| 214 |
- |
|
| 215 |
- mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
|
|
| 216 |
- Source: &p.RuntimeSourcePath, |
|
| 217 |
- Destination: defaultPluginRuntimeDestination, |
|
| 218 |
- Type: "bind", |
|
| 219 |
- Options: []string{"rbind", "rshared"},
|
|
| 220 |
- }) |
|
| 221 |
- for _, mount := range mounts {
|
|
| 222 |
- m := specs.Mount{
|
|
| 223 |
- Destination: mount.Destination, |
|
| 224 |
- Type: mount.Type, |
|
| 225 |
- Options: mount.Options, |
|
| 226 |
- } |
|
| 227 |
- // TODO: if nil, then it's required and user didn't set it |
|
| 228 |
- if mount.Source != nil {
|
|
| 229 |
- m.Source = *mount.Source |
|
| 230 |
- } |
|
| 231 |
- if m.Source != "" && m.Type == "bind" {
|
|
| 232 |
- fi, err := os.Lstat(filepath.Join(rootfs, string(os.PathSeparator), m.Destination)) // TODO: followsymlinks |
|
| 233 |
- if err != nil {
|
|
| 234 |
- return nil, err |
|
| 235 |
- } |
|
| 236 |
- if fi.IsDir() {
|
|
| 237 |
- if err := os.MkdirAll(m.Source, 0700); err != nil {
|
|
| 238 |
- return nil, err |
|
| 239 |
- } |
|
| 240 |
- } |
|
| 241 |
- } |
|
| 242 |
- s.Mounts = append(s.Mounts, m) |
|
| 243 |
- } |
|
| 244 |
- |
|
| 245 |
- envs := make([]string, 1, len(p.PluginObj.Config.Env)+1) |
|
| 246 |
- envs[0] = "PATH=" + system.DefaultPathEnv |
|
| 247 |
- envs = append(envs, p.PluginObj.Config.Env...) |
|
| 248 |
- |
|
| 249 |
- args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...) |
|
| 250 |
- cwd := p.PluginObj.Manifest.Workdir |
|
| 251 |
- if len(cwd) == 0 {
|
|
| 252 |
- cwd = "/" |
|
| 253 |
- } |
|
| 254 |
- s.Process = specs.Process{
|
|
| 255 |
- Terminal: false, |
|
| 256 |
- Args: args, |
|
| 257 |
- Cwd: cwd, |
|
| 258 |
- Env: envs, |
|
| 259 |
- } |
|
| 260 |
- |
|
| 261 |
- return &s, nil |
|
| 262 |
-} |
| 263 | 40 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,249 @@ |
| 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 |
+ |
|
| 12 |
+ "github.com/docker/docker/api/types" |
|
| 13 |
+ "github.com/docker/docker/pkg/plugins" |
|
| 14 |
+ "github.com/docker/docker/pkg/system" |
|
| 15 |
+ "github.com/opencontainers/runtime-spec/specs-go" |
|
| 16 |
+) |
|
| 17 |
+ |
|
| 18 |
+const defaultPluginRuntimeDestination = "/run/docker/plugins" |
|
| 19 |
+ |
|
| 20 |
+// ErrInadequateCapability indicates that the plugin did not have the requested capability. |
|
| 21 |
+type ErrInadequateCapability string |
|
| 22 |
+ |
|
| 23 |
+func (cap ErrInadequateCapability) Error() string {
|
|
| 24 |
+ return fmt.Sprintf("plugin does not provide %q capability", cap)
|
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+func newPluginObj(name, id, tag string) types.Plugin {
|
|
| 28 |
+ return types.Plugin{Name: name, ID: id, Tag: tag}
|
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+// NewPlugin creates a plugin. |
|
| 32 |
+func NewPlugin(name, id, runRoot, tag string) *Plugin {
|
|
| 33 |
+ return &Plugin{
|
|
| 34 |
+ PluginObj: newPluginObj(name, id, tag), |
|
| 35 |
+ RuntimeSourcePath: filepath.Join(runRoot, id), |
|
| 36 |
+ } |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+// Client returns the plugin client. |
|
| 40 |
+func (p *Plugin) Client() *plugins.Client {
|
|
| 41 |
+ return p.PClient |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+// IsV1 returns true for V1 plugins and false otherwise. |
|
| 45 |
+func (p *Plugin) IsV1() bool {
|
|
| 46 |
+ return false |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+// Name returns the plugin name. |
|
| 50 |
+func (p *Plugin) Name() string {
|
|
| 51 |
+ name := p.PluginObj.Name |
|
| 52 |
+ if len(p.PluginObj.Tag) > 0 {
|
|
| 53 |
+ // TODO: this feels hacky, maybe we should be storing the distribution reference rather than splitting these |
|
| 54 |
+ name += ":" + p.PluginObj.Tag |
|
| 55 |
+ } |
|
| 56 |
+ return name |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+// FilterByCap query the plugin for a given capability. |
|
| 60 |
+func (p *Plugin) FilterByCap(capability string) (*Plugin, error) {
|
|
| 61 |
+ capability = strings.ToLower(capability) |
|
| 62 |
+ for _, typ := range p.PluginObj.Manifest.Interface.Types {
|
|
| 63 |
+ if typ.Capability == capability && typ.Prefix == "docker" {
|
|
| 64 |
+ return p, nil |
|
| 65 |
+ } |
|
| 66 |
+ } |
|
| 67 |
+ return nil, ErrInadequateCapability(capability) |
|
| 68 |
+} |
|
| 69 |
+ |
|
| 70 |
+// RemoveFromDisk deletes the plugin's runtime files from disk. |
|
| 71 |
+func (p *Plugin) RemoveFromDisk() error {
|
|
| 72 |
+ return os.RemoveAll(p.RuntimeSourcePath) |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+// InitPlugin populates the plugin object from the plugin manifest file. |
|
| 76 |
+func (p *Plugin) InitPlugin(libRoot string) error {
|
|
| 77 |
+ dt, err := os.Open(filepath.Join(libRoot, p.PluginObj.ID, "manifest.json")) |
|
| 78 |
+ if err != nil {
|
|
| 79 |
+ return err |
|
| 80 |
+ } |
|
| 81 |
+ err = json.NewDecoder(dt).Decode(&p.PluginObj.Manifest) |
|
| 82 |
+ dt.Close() |
|
| 83 |
+ if err != nil {
|
|
| 84 |
+ return err |
|
| 85 |
+ } |
|
| 86 |
+ |
|
| 87 |
+ p.PluginObj.Config.Mounts = make([]types.PluginMount, len(p.PluginObj.Manifest.Mounts)) |
|
| 88 |
+ for i, mount := range p.PluginObj.Manifest.Mounts {
|
|
| 89 |
+ p.PluginObj.Config.Mounts[i] = mount |
|
| 90 |
+ } |
|
| 91 |
+ p.PluginObj.Config.Env = make([]string, 0, len(p.PluginObj.Manifest.Env)) |
|
| 92 |
+ for _, env := range p.PluginObj.Manifest.Env {
|
|
| 93 |
+ if env.Value != nil {
|
|
| 94 |
+ p.PluginObj.Config.Env = append(p.PluginObj.Config.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
|
|
| 95 |
+ } |
|
| 96 |
+ } |
|
| 97 |
+ copy(p.PluginObj.Config.Args, p.PluginObj.Manifest.Args.Value) |
|
| 98 |
+ |
|
| 99 |
+ f, err := os.Create(filepath.Join(libRoot, p.PluginObj.ID, "plugin-config.json")) |
|
| 100 |
+ if err != nil {
|
|
| 101 |
+ return err |
|
| 102 |
+ } |
|
| 103 |
+ err = json.NewEncoder(f).Encode(&p.PluginObj.Config) |
|
| 104 |
+ f.Close() |
|
| 105 |
+ return err |
|
| 106 |
+} |
|
| 107 |
+ |
|
| 108 |
+// Set is used to pass arguments to the plugin. |
|
| 109 |
+func (p *Plugin) Set(args []string) error {
|
|
| 110 |
+ m := make(map[string]string, len(args)) |
|
| 111 |
+ for _, arg := range args {
|
|
| 112 |
+ i := strings.Index(arg, "=") |
|
| 113 |
+ if i < 0 {
|
|
| 114 |
+ return fmt.Errorf("No equal sign '=' found in %s", arg)
|
|
| 115 |
+ } |
|
| 116 |
+ m[arg[:i]] = arg[i+1:] |
|
| 117 |
+ } |
|
| 118 |
+ return errors.New("not implemented")
|
|
| 119 |
+} |
|
| 120 |
+ |
|
| 121 |
+// ComputePrivileges takes the manifest file and computes the list of access necessary |
|
| 122 |
+// for the plugin on the host. |
|
| 123 |
+func (p *Plugin) ComputePrivileges() types.PluginPrivileges {
|
|
| 124 |
+ m := p.PluginObj.Manifest |
|
| 125 |
+ var privileges types.PluginPrivileges |
|
| 126 |
+ if m.Network.Type != "null" && m.Network.Type != "bridge" {
|
|
| 127 |
+ privileges = append(privileges, types.PluginPrivilege{
|
|
| 128 |
+ Name: "network", |
|
| 129 |
+ Description: "", |
|
| 130 |
+ Value: []string{m.Network.Type},
|
|
| 131 |
+ }) |
|
| 132 |
+ } |
|
| 133 |
+ for _, mount := range m.Mounts {
|
|
| 134 |
+ if mount.Source != nil {
|
|
| 135 |
+ privileges = append(privileges, types.PluginPrivilege{
|
|
| 136 |
+ Name: "mount", |
|
| 137 |
+ Description: "", |
|
| 138 |
+ Value: []string{*mount.Source},
|
|
| 139 |
+ }) |
|
| 140 |
+ } |
|
| 141 |
+ } |
|
| 142 |
+ for _, device := range m.Devices {
|
|
| 143 |
+ if device.Path != nil {
|
|
| 144 |
+ privileges = append(privileges, types.PluginPrivilege{
|
|
| 145 |
+ Name: "device", |
|
| 146 |
+ Description: "", |
|
| 147 |
+ Value: []string{*device.Path},
|
|
| 148 |
+ }) |
|
| 149 |
+ } |
|
| 150 |
+ } |
|
| 151 |
+ if len(m.Capabilities) > 0 {
|
|
| 152 |
+ privileges = append(privileges, types.PluginPrivilege{
|
|
| 153 |
+ Name: "capabilities", |
|
| 154 |
+ Description: "", |
|
| 155 |
+ Value: m.Capabilities, |
|
| 156 |
+ }) |
|
| 157 |
+ } |
|
| 158 |
+ return privileges |
|
| 159 |
+} |
|
| 160 |
+ |
|
| 161 |
+// IsEnabled returns the active state of the plugin. |
|
| 162 |
+func (p *Plugin) IsEnabled() bool {
|
|
| 163 |
+ p.RLock() |
|
| 164 |
+ defer p.RUnlock() |
|
| 165 |
+ |
|
| 166 |
+ return p.PluginObj.Enabled |
|
| 167 |
+} |
|
| 168 |
+ |
|
| 169 |
+// GetID returns the plugin's ID. |
|
| 170 |
+func (p *Plugin) GetID() string {
|
|
| 171 |
+ p.RLock() |
|
| 172 |
+ defer p.RUnlock() |
|
| 173 |
+ |
|
| 174 |
+ return p.PluginObj.ID |
|
| 175 |
+} |
|
| 176 |
+ |
|
| 177 |
+// GetSocket returns the plugin socket. |
|
| 178 |
+func (p *Plugin) GetSocket() string {
|
|
| 179 |
+ p.RLock() |
|
| 180 |
+ defer p.RUnlock() |
|
| 181 |
+ |
|
| 182 |
+ return p.PluginObj.Manifest.Interface.Socket |
|
| 183 |
+} |
|
| 184 |
+ |
|
| 185 |
+// GetTypes returns the interface types of a plugin. |
|
| 186 |
+func (p *Plugin) GetTypes() []types.PluginInterfaceType {
|
|
| 187 |
+ p.RLock() |
|
| 188 |
+ defer p.RUnlock() |
|
| 189 |
+ |
|
| 190 |
+ return p.PluginObj.Manifest.Interface.Types |
|
| 191 |
+} |
|
| 192 |
+ |
|
| 193 |
+// InitSpec creates an OCI spec from the plugin's config. |
|
| 194 |
+func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
|
|
| 195 |
+ rootfs := filepath.Join(libRoot, p.PluginObj.ID, "rootfs") |
|
| 196 |
+ s.Root = specs.Root{
|
|
| 197 |
+ Path: rootfs, |
|
| 198 |
+ Readonly: false, // TODO: all plugins should be readonly? settable in manifest? |
|
| 199 |
+ } |
|
| 200 |
+ |
|
| 201 |
+ mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
|
|
| 202 |
+ Source: &p.RuntimeSourcePath, |
|
| 203 |
+ Destination: defaultPluginRuntimeDestination, |
|
| 204 |
+ Type: "bind", |
|
| 205 |
+ Options: []string{"rbind", "rshared"},
|
|
| 206 |
+ }) |
|
| 207 |
+ for _, mount := range mounts {
|
|
| 208 |
+ m := specs.Mount{
|
|
| 209 |
+ Destination: mount.Destination, |
|
| 210 |
+ Type: mount.Type, |
|
| 211 |
+ Options: mount.Options, |
|
| 212 |
+ } |
|
| 213 |
+ // TODO: if nil, then it's required and user didn't set it |
|
| 214 |
+ if mount.Source != nil {
|
|
| 215 |
+ m.Source = *mount.Source |
|
| 216 |
+ } |
|
| 217 |
+ if m.Source != "" && m.Type == "bind" {
|
|
| 218 |
+ fi, err := os.Lstat(filepath.Join(rootfs, m.Destination)) // TODO: followsymlinks |
|
| 219 |
+ if err != nil {
|
|
| 220 |
+ return nil, err |
|
| 221 |
+ } |
|
| 222 |
+ if fi.IsDir() {
|
|
| 223 |
+ if err := os.MkdirAll(m.Source, 0700); err != nil {
|
|
| 224 |
+ return nil, err |
|
| 225 |
+ } |
|
| 226 |
+ } |
|
| 227 |
+ } |
|
| 228 |
+ s.Mounts = append(s.Mounts, m) |
|
| 229 |
+ } |
|
| 230 |
+ |
|
| 231 |
+ envs := make([]string, 1, len(p.PluginObj.Config.Env)+1) |
|
| 232 |
+ envs[0] = "PATH=" + system.DefaultPathEnv |
|
| 233 |
+ envs = append(envs, p.PluginObj.Config.Env...) |
|
| 234 |
+ |
|
| 235 |
+ args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...) |
|
| 236 |
+ cwd := p.PluginObj.Manifest.Workdir |
|
| 237 |
+ if len(cwd) == 0 {
|
|
| 238 |
+ cwd = "/" |
|
| 239 |
+ } |
|
| 240 |
+ s.Process = specs.Process{
|
|
| 241 |
+ Terminal: false, |
|
| 242 |
+ Args: args, |
|
| 243 |
+ Cwd: cwd, |
|
| 244 |
+ Env: envs, |
|
| 245 |
+ } |
|
| 246 |
+ |
|
| 247 |
+ return &s, nil |
|
| 248 |
+} |
| ... | ... |
@@ -7,14 +7,17 @@ import ( |
| 7 | 7 |
"sync" |
| 8 | 8 |
|
| 9 | 9 |
"github.com/docker/docker/pkg/locker" |
| 10 |
- pluginStore "github.com/docker/docker/plugin/store" |
|
| 10 |
+ "github.com/docker/docker/plugin/getter" |
|
| 11 | 11 |
"github.com/docker/docker/volume" |
| 12 | 12 |
) |
| 13 | 13 |
|
| 14 | 14 |
// currently created by hand. generation tool would generate this like: |
| 15 | 15 |
// $ extpoint-gen Driver > volume/extpoint.go |
| 16 | 16 |
|
| 17 |
-var drivers = &driverExtpoint{extensions: make(map[string]volume.Driver), driverLock: &locker.Locker{}}
|
|
| 17 |
+var drivers = &driverExtpoint{
|
|
| 18 |
+ extensions: make(map[string]volume.Driver), |
|
| 19 |
+ driverLock: &locker.Locker{},
|
|
| 20 |
+} |
|
| 18 | 21 |
|
| 19 | 22 |
const extName = "VolumeDriver" |
| 20 | 23 |
|
| ... | ... |
@@ -49,7 +52,13 @@ type volumeDriver interface {
|
| 49 | 49 |
type driverExtpoint struct {
|
| 50 | 50 |
extensions map[string]volume.Driver |
| 51 | 51 |
sync.Mutex |
| 52 |
- driverLock *locker.Locker |
|
| 52 |
+ driverLock *locker.Locker |
|
| 53 |
+ plugingetter getter.PluginGetter |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+// RegisterPluginGetter sets the plugingetter |
|
| 57 |
+func RegisterPluginGetter(plugingetter getter.PluginGetter) {
|
|
| 58 |
+ drivers.plugingetter = plugingetter |
|
| 53 | 59 |
} |
| 54 | 60 |
|
| 55 | 61 |
// Register associates the given driver to the given name, checking if |
| ... | ... |
@@ -72,6 +81,7 @@ func Register(extension volume.Driver, name string) bool {
|
| 72 | 72 |
} |
| 73 | 73 |
|
| 74 | 74 |
drivers.extensions[name] = extension |
| 75 |
+ |
|
| 75 | 76 |
return true |
| 76 | 77 |
} |
| 77 | 78 |
|
| ... | ... |
@@ -102,7 +112,7 @@ func lookup(name string, mode int) (volume.Driver, error) {
|
| 102 | 102 |
return ext, nil |
| 103 | 103 |
} |
| 104 | 104 |
|
| 105 |
- p, err := pluginStore.LookupWithCapability(name, extName, mode) |
|
| 105 |
+ p, err := drivers.plugingetter.Get(name, extName, mode) |
|
| 106 | 106 |
if err != nil {
|
| 107 | 107 |
return nil, fmt.Errorf("Error looking up volume plugin %s: %v", name, err)
|
| 108 | 108 |
} |
| ... | ... |
@@ -112,7 +122,7 @@ func lookup(name string, mode int) (volume.Driver, error) {
|
| 112 | 112 |
return nil, err |
| 113 | 113 |
} |
| 114 | 114 |
|
| 115 |
- if p.IsLegacy() {
|
|
| 115 |
+ if p.IsV1() {
|
|
| 116 | 116 |
drivers.Lock() |
| 117 | 117 |
drivers.extensions[name] = d |
| 118 | 118 |
drivers.Unlock() |
| ... | ... |
@@ -134,7 +144,7 @@ func GetDriver(name string) (volume.Driver, error) {
|
| 134 | 134 |
if name == "" {
|
| 135 | 135 |
name = volume.DefaultDriverName |
| 136 | 136 |
} |
| 137 |
- return lookup(name, pluginStore.LOOKUP) |
|
| 137 |
+ return lookup(name, getter.LOOKUP) |
|
| 138 | 138 |
} |
| 139 | 139 |
|
| 140 | 140 |
// CreateDriver returns a volume driver by its name and increments RefCount. |
| ... | ... |
@@ -143,7 +153,7 @@ func CreateDriver(name string) (volume.Driver, error) {
|
| 143 | 143 |
if name == "" {
|
| 144 | 144 |
name = volume.DefaultDriverName |
| 145 | 145 |
} |
| 146 |
- return lookup(name, pluginStore.CREATE) |
|
| 146 |
+ return lookup(name, getter.CREATE) |
|
| 147 | 147 |
} |
| 148 | 148 |
|
| 149 | 149 |
// RemoveDriver returns a volume driver by its name and decrements RefCount.. |
| ... | ... |
@@ -152,7 +162,7 @@ func RemoveDriver(name string) (volume.Driver, error) {
|
| 152 | 152 |
if name == "" {
|
| 153 | 153 |
name = volume.DefaultDriverName |
| 154 | 154 |
} |
| 155 |
- return lookup(name, pluginStore.REMOVE) |
|
| 155 |
+ return lookup(name, getter.REMOVE) |
|
| 156 | 156 |
} |
| 157 | 157 |
|
| 158 | 158 |
// GetDriverList returns list of volume drivers registered. |
| ... | ... |
@@ -169,7 +179,7 @@ func GetDriverList() []string {
|
| 169 | 169 |
|
| 170 | 170 |
// GetAllDrivers lists all the registered drivers |
| 171 | 171 |
func GetAllDrivers() ([]volume.Driver, error) {
|
| 172 |
- plugins, err := pluginStore.FindWithCapability(extName) |
|
| 172 |
+ plugins, err := drivers.plugingetter.GetAllByCap(extName) |
|
| 173 | 173 |
if err != nil {
|
| 174 | 174 |
return nil, fmt.Errorf("error listing plugins: %v", err)
|
| 175 | 175 |
} |
| ... | ... |
@@ -190,7 +200,7 @@ func GetAllDrivers() ([]volume.Driver, error) {
|
| 190 | 190 |
} |
| 191 | 191 |
|
| 192 | 192 |
ext = NewVolumeDriver(name, p.Client()) |
| 193 |
- if p.IsLegacy() {
|
|
| 193 |
+ if p.IsV1() {
|
|
| 194 | 194 |
drivers.extensions[name] = ext |
| 195 | 195 |
} |
| 196 | 196 |
ds = append(ds, ext) |
| ... | ... |
@@ -3,16 +3,20 @@ package volumedrivers |
| 3 | 3 |
import ( |
| 4 | 4 |
"testing" |
| 5 | 5 |
|
| 6 |
+ pluginstore "github.com/docker/docker/plugin/store" |
|
| 6 | 7 |
volumetestutils "github.com/docker/docker/volume/testutils" |
| 7 | 8 |
) |
| 8 | 9 |
|
| 9 | 10 |
func TestGetDriver(t *testing.T) {
|
| 11 |
+ pluginStore := pluginstore.NewStore("/var/lib/docker")
|
|
| 12 |
+ RegisterPluginGetter(pluginStore) |
|
| 13 |
+ |
|
| 10 | 14 |
_, err := GetDriver("missing")
|
| 11 | 15 |
if err == nil {
|
| 12 | 16 |
t.Fatal("Expected error, was nil")
|
| 13 | 17 |
} |
| 14 |
- |
|
| 15 | 18 |
Register(volumetestutils.NewFakeDriver("fake"), "fake")
|
| 19 |
+ |
|
| 16 | 20 |
d, err := GetDriver("fake")
|
| 17 | 21 |
if err != nil {
|
| 18 | 22 |
t.Fatal(err) |
| ... | ... |
@@ -5,11 +5,15 @@ import ( |
| 5 | 5 |
"strings" |
| 6 | 6 |
"testing" |
| 7 | 7 |
|
| 8 |
+ pluginstore "github.com/docker/docker/plugin/store" |
|
| 8 | 9 |
"github.com/docker/docker/volume/drivers" |
| 9 | 10 |
volumetestutils "github.com/docker/docker/volume/testutils" |
| 10 | 11 |
) |
| 11 | 12 |
|
| 12 | 13 |
func TestCreate(t *testing.T) {
|
| 14 |
+ pluginStore := pluginstore.NewStore("/var/lib/docker")
|
|
| 15 |
+ volumedrivers.RegisterPluginGetter(pluginStore) |
|
| 16 |
+ |
|
| 13 | 17 |
volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
|
| 14 | 18 |
defer volumedrivers.Unregister("fake")
|
| 15 | 19 |
s, err := New("")
|