|
@@ -17,7 +17,6 @@ import (
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/image"
|
|
"github.com/docker/docker/image"
|
|
"github.com/docker/docker/layer"
|
|
"github.com/docker/docker/layer"
|
|
- "github.com/docker/docker/libcontainerd"
|
|
|
|
"github.com/docker/docker/pkg/authorization"
|
|
"github.com/docker/docker/pkg/authorization"
|
|
"github.com/docker/docker/pkg/ioutils"
|
|
"github.com/docker/docker/pkg/ioutils"
|
|
"github.com/docker/docker/pkg/mount"
|
|
"github.com/docker/docker/pkg/mount"
|
|
@@ -26,6 +25,7 @@ import (
|
|
"github.com/docker/docker/plugin/v2"
|
|
"github.com/docker/docker/plugin/v2"
|
|
"github.com/docker/docker/registry"
|
|
"github.com/docker/docker/registry"
|
|
"github.com/opencontainers/go-digest"
|
|
"github.com/opencontainers/go-digest"
|
|
|
|
+ specs "github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/pkg/errors"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
)
|
|
@@ -35,6 +35,14 @@ const rootFSFileName = "rootfs"
|
|
|
|
|
|
var validFullID = regexp.MustCompile(`^([a-f0-9]{64})$`)
|
|
var validFullID = regexp.MustCompile(`^([a-f0-9]{64})$`)
|
|
|
|
|
|
|
|
+// Executor is the interface that the plugin manager uses to interact with for starting/stopping plugins
|
|
|
|
+type Executor interface {
|
|
|
|
+ Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error
|
|
|
|
+ Restore(id string, stdout, stderr io.WriteCloser) error
|
|
|
|
+ IsRunning(id string) (bool, error)
|
|
|
|
+ Signal(id string, signal int) error
|
|
|
|
+}
|
|
|
|
+
|
|
func (pm *Manager) restorePlugin(p *v2.Plugin) error {
|
|
func (pm *Manager) restorePlugin(p *v2.Plugin) error {
|
|
if p.IsEnabled() {
|
|
if p.IsEnabled() {
|
|
return pm.restore(p)
|
|
return pm.restore(p)
|
|
@@ -47,24 +55,27 @@ type eventLogger func(id, name, action string)
|
|
// ManagerConfig defines configuration needed to start new manager.
|
|
// ManagerConfig defines configuration needed to start new manager.
|
|
type ManagerConfig struct {
|
|
type ManagerConfig struct {
|
|
Store *Store // remove
|
|
Store *Store // remove
|
|
- Executor libcontainerd.Remote
|
|
|
|
RegistryService registry.Service
|
|
RegistryService registry.Service
|
|
LiveRestoreEnabled bool // TODO: remove
|
|
LiveRestoreEnabled bool // TODO: remove
|
|
LogPluginEvent eventLogger
|
|
LogPluginEvent eventLogger
|
|
Root string
|
|
Root string
|
|
ExecRoot string
|
|
ExecRoot string
|
|
|
|
+ CreateExecutor ExecutorCreator
|
|
AuthzMiddleware *authorization.Middleware
|
|
AuthzMiddleware *authorization.Middleware
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// ExecutorCreator is used in the manager config to pass in an `Executor`
|
|
|
|
+type ExecutorCreator func(*Manager) (Executor, error)
|
|
|
|
+
|
|
// Manager controls the plugin subsystem.
|
|
// Manager controls the plugin subsystem.
|
|
type Manager struct {
|
|
type Manager struct {
|
|
- config ManagerConfig
|
|
|
|
- mu sync.RWMutex // protects cMap
|
|
|
|
- muGC sync.RWMutex // protects blobstore deletions
|
|
|
|
- cMap map[*v2.Plugin]*controller
|
|
|
|
- containerdClient libcontainerd.Client
|
|
|
|
- blobStore *basicBlobStore
|
|
|
|
- publisher *pubsub.Publisher
|
|
|
|
|
|
+ config ManagerConfig
|
|
|
|
+ mu sync.RWMutex // protects cMap
|
|
|
|
+ muGC sync.RWMutex // protects blobstore deletions
|
|
|
|
+ cMap map[*v2.Plugin]*controller
|
|
|
|
+ blobStore *basicBlobStore
|
|
|
|
+ publisher *pubsub.Publisher
|
|
|
|
+ executor Executor
|
|
}
|
|
}
|
|
|
|
|
|
// controller represents the manager's control on a plugin.
|
|
// controller represents the manager's control on a plugin.
|
|
@@ -111,10 +122,11 @@ func NewManager(config ManagerConfig) (*Manager, error) {
|
|
}
|
|
}
|
|
|
|
|
|
var err error
|
|
var err error
|
|
- manager.containerdClient, err = config.Executor.Client(manager) // todo: move to another struct
|
|
|
|
|
|
+ manager.executor, err = config.CreateExecutor(manager)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return nil, errors.Wrap(err, "failed to create containerd client")
|
|
|
|
|
|
+ return nil, err
|
|
}
|
|
}
|
|
|
|
+
|
|
manager.blobStore, err = newBasicBlobStore(filepath.Join(manager.config.Root, "storage/blobs"))
|
|
manager.blobStore, err = newBasicBlobStore(filepath.Join(manager.config.Root, "storage/blobs"))
|
|
if err != nil {
|
|
if err != nil {
|
|
return nil, err
|
|
return nil, err
|
|
@@ -133,42 +145,37 @@ func (pm *Manager) tmpDir() string {
|
|
return filepath.Join(pm.config.Root, "tmp")
|
|
return filepath.Join(pm.config.Root, "tmp")
|
|
}
|
|
}
|
|
|
|
|
|
-// StateChanged updates plugin internals using libcontainerd events.
|
|
|
|
-func (pm *Manager) StateChanged(id string, e libcontainerd.StateInfo) error {
|
|
|
|
- logrus.Debugf("plugin state changed %s %#v", id, e)
|
|
|
|
-
|
|
|
|
- switch e.State {
|
|
|
|
- case libcontainerd.StateExit:
|
|
|
|
- p, err := pm.config.Store.GetV2Plugin(id)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
|
|
+// HandleExitEvent is called when the executor receives the exit event
|
|
|
|
+// In the future we may change this, but for now all we care about is the exit event.
|
|
|
|
+func (pm *Manager) HandleExitEvent(id string) error {
|
|
|
|
+ p, err := pm.config.Store.GetV2Plugin(id)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
|
|
- os.RemoveAll(filepath.Join(pm.config.ExecRoot, id))
|
|
|
|
|
|
+ os.RemoveAll(filepath.Join(pm.config.ExecRoot, id))
|
|
|
|
|
|
- if p.PropagatedMount != "" {
|
|
|
|
- if err := mount.Unmount(p.PropagatedMount); err != nil {
|
|
|
|
- logrus.Warnf("Could not unmount %s: %v", p.PropagatedMount, err)
|
|
|
|
- }
|
|
|
|
- propRoot := filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount")
|
|
|
|
- if err := mount.Unmount(propRoot); err != nil {
|
|
|
|
- logrus.Warn("Could not unmount %s: %v", propRoot, err)
|
|
|
|
- }
|
|
|
|
|
|
+ if p.PropagatedMount != "" {
|
|
|
|
+ if err := mount.Unmount(p.PropagatedMount); err != nil {
|
|
|
|
+ logrus.Warnf("Could not unmount %s: %v", p.PropagatedMount, err)
|
|
}
|
|
}
|
|
-
|
|
|
|
- pm.mu.RLock()
|
|
|
|
- c := pm.cMap[p]
|
|
|
|
- if c.exitChan != nil {
|
|
|
|
- close(c.exitChan)
|
|
|
|
|
|
+ propRoot := filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount")
|
|
|
|
+ if err := mount.Unmount(propRoot); err != nil {
|
|
|
|
+ logrus.Warn("Could not unmount %s: %v", propRoot, err)
|
|
}
|
|
}
|
|
- restart := c.restart
|
|
|
|
- pm.mu.RUnlock()
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- if restart {
|
|
|
|
- pm.enable(p, c, true)
|
|
|
|
- }
|
|
|
|
|
|
+ pm.mu.RLock()
|
|
|
|
+ c := pm.cMap[p]
|
|
|
|
+ if c.exitChan != nil {
|
|
|
|
+ close(c.exitChan)
|
|
}
|
|
}
|
|
|
|
+ restart := c.restart
|
|
|
|
+ pm.mu.RUnlock()
|
|
|
|
|
|
|
|
+ if restart {
|
|
|
|
+ pm.enable(p, c, true)
|
|
|
|
+ }
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
@@ -333,23 +340,10 @@ func (l logHook) Fire(entry *logrus.Entry) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func attachToLog(id string) func(libcontainerd.IOPipe) error {
|
|
|
|
- return func(iop libcontainerd.IOPipe) error {
|
|
|
|
- iop.Stdin.Close()
|
|
|
|
-
|
|
|
|
- logger := logrus.New()
|
|
|
|
- logger.Hooks.Add(logHook{id})
|
|
|
|
- // TODO: cache writer per id
|
|
|
|
- w := logger.Writer()
|
|
|
|
- go func() {
|
|
|
|
- io.Copy(w, iop.Stdout)
|
|
|
|
- }()
|
|
|
|
- go func() {
|
|
|
|
- // TODO: update logrus and use logger.WriterLevel
|
|
|
|
- io.Copy(w, iop.Stderr)
|
|
|
|
- }()
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
|
|
+func makeLoggerStreams(id string) (stdout, stderr io.WriteCloser) {
|
|
|
|
+ logger := logrus.New()
|
|
|
|
+ logger.Hooks.Add(logHook{id})
|
|
|
|
+ return logger.WriterLevel(logrus.InfoLevel), logger.WriterLevel(logrus.ErrorLevel)
|
|
}
|
|
}
|
|
|
|
|
|
func validatePrivileges(requiredPrivileges, privileges types.PluginPrivileges) error {
|
|
func validatePrivileges(requiredPrivileges, privileges types.PluginPrivileges) error {
|