Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: add plugin framework #919

Merged
merged 1 commit into from
Mar 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apis/plugins/ContainerPlugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package plugins

import "io"

// ContainerPlugin defines in which place a plugin will be triggered in container lifecycle
type ContainerPlugin interface {
// PreCreate defines plugin point where recevives an container create request, in this plugin point user
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/recevives/receives

// could change the container create body passed-in by http request body
PreCreate(io.ReadCloser) (io.ReadCloser, error)

// PreStart returns an array of priority and args which will pass to runc, the every priority
// used to sort the pre start array that pass to runc, network plugin hook always has priority value 0.
PreStart(interface{}) ([]int, [][]string, error)
}
12 changes: 12 additions & 0 deletions apis/plugins/DaemonPlugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package plugins

// DaemonPlugin defines in which place does pouch daemon support plugin
type DaemonPlugin interface {
// PreStartHook is invoked by pouch daemon before real start, in this hook user could start dfget proxy or other
// standalone process plugins
PreStartHook() error

// PreStopHook is invoked by pouch daemon before daemon process exit, not a promise if daemon is killed, in this
// hook user could stop the process or plugin started by PreStartHook
PreStopHook() error
}
12 changes: 11 additions & 1 deletion apis/server/container_bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (

"github.com/go-openapi/strfmt"
"github.com/gorilla/mux"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)

func (s *Server) removeContainers(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
Expand Down Expand Up @@ -114,8 +116,16 @@ func (s *Server) startContainerExec(ctx context.Context, rw http.ResponseWriter,

func (s *Server) createContainer(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
config := &types.ContainerCreateConfig{}
reader := req.Body
var ex error
if s.ContainerPlugin != nil {
logrus.Infof("invoke container pre-create hook in plugin")
if reader, ex = s.ContainerPlugin.PreCreate(req.Body); ex != nil {
return errors.Wrapf(ex, "pre-create plugin piont execute failed")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/piont/point

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these typo will be fixed in #929

}
}
// decode request body
if err := json.NewDecoder(req.Body).Decode(config); err != nil {
if err := json.NewDecoder(reader).Decode(config); err != nil {
return httputils.NewHTTPError(err, http.StatusBadRequest)
}
// validate request body
Expand Down
16 changes: 9 additions & 7 deletions apis/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"
"syscall"

"github.com/alibaba/pouch/apis/plugins"
"github.com/alibaba/pouch/daemon/config"
"github.com/alibaba/pouch/daemon/mgr"

Expand All @@ -18,13 +19,14 @@ import (

// Server is a http server which serves restful api to client.
type Server struct {
Config config.Config
ContainerMgr mgr.ContainerMgr
SystemMgr mgr.SystemMgr
ImageMgr mgr.ImageMgr
VolumeMgr mgr.VolumeMgr
NetworkMgr mgr.NetworkMgr
listeners []net.Listener
Config config.Config
ContainerMgr mgr.ContainerMgr
SystemMgr mgr.SystemMgr
ImageMgr mgr.ImageMgr
VolumeMgr mgr.VolumeMgr
NetworkMgr mgr.NetworkMgr
listeners []net.Listener
ContainerPlugin plugins.ContainerPlugin
}

// Start setup route table and listen to specified address which currently only supports unix socket and tcp address.
Expand Down
3 changes: 3 additions & 0 deletions daemon/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,7 @@ type Config struct {

// CgroupParent is to set parent cgroup for all containers
CgroupParent string `json:"cgroup-parent,omitempty"`

// PluginPath is set the path where plugin so file put
PluginPath string `json:"plugin"`
}
105 changes: 88 additions & 17 deletions daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package daemon

import (
"context"
"fmt"
"path"
"plugin"
"reflect"

"github.com/alibaba/pouch/apis/plugins"
"github.com/alibaba/pouch/apis/server"
cri "github.com/alibaba/pouch/cri/service"
"github.com/alibaba/pouch/ctrd"
Expand All @@ -15,22 +18,25 @@ import (
"github.com/alibaba/pouch/pkg/meta"

"github.com/gorilla/mux"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)

// Daemon refers to a daemon.
type Daemon struct {
config config.Config
containerStore *meta.Store
containerd *ctrd.Client
containerMgr mgr.ContainerMgr
systemMgr mgr.SystemMgr
imageMgr mgr.ImageMgr
volumeMgr mgr.VolumeMgr
networkMgr mgr.NetworkMgr
criMgr mgr.CriMgr
server server.Server
criService *cri.Service
config config.Config
containerStore *meta.Store
containerd *ctrd.Client
containerMgr mgr.ContainerMgr
systemMgr mgr.SystemMgr
imageMgr mgr.ImageMgr
volumeMgr mgr.VolumeMgr
networkMgr mgr.NetworkMgr
criMgr mgr.CriMgr
server server.Server
criService *cri.Service
containerPlugin plugins.ContainerPlugin
daemonPlugin plugins.DaemonPlugin
}

// router represents the router of daemon.
Expand Down Expand Up @@ -71,11 +77,58 @@ func NewDaemon(cfg config.Config) *Daemon {
}
}

func loadSymbolByName(p *plugin.Plugin, name string) (plugin.Symbol, error) {
s, err := p.Lookup(name)
if err != nil {
return nil, errors.Wrapf(err, "lookup plugin with name %s error", name)
}
return s, nil
}

// Run starts daemon.
func (d *Daemon) Run() error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

var s plugin.Symbol
var err error

if d.config.PluginPath != "" {
p, err := plugin.Open(d.config.PluginPath)
if err != nil {
return errors.Wrapf(err, "load plugin at %s error", d.config.PluginPath)
}

//load container plugin if exist
if s, err = loadSymbolByName(p, "DaemonPlugin"); err != nil {
return err
}
if daemonPlugin, ok := s.(plugins.DaemonPlugin); ok {
logrus.Infof("setup daemon plugin from %s", d.config.PluginPath)
d.daemonPlugin = daemonPlugin
} else if s != nil {
return fmt.Errorf("not a container plugin at %s %q", d.config.PluginPath, s)
}

//load container plugin if exist
if s, err = loadSymbolByName(p, "ContainerPlugin"); err != nil {
return err
}
if containerPlugin, ok := s.(plugins.ContainerPlugin); ok {
logrus.Infof("setup container plugin from %s", d.config.PluginPath)
d.containerPlugin = containerPlugin
} else if s != nil {
return fmt.Errorf("not a container plugin at %s %q", d.config.PluginPath, s)
}
}

if d.daemonPlugin != nil {
logrus.Infof("invoke pre-start hook in plugin")
if err = d.daemonPlugin.PreStartHook(); err != nil {
return err
}
}

imageMgr, err := internal.GenImageMgr(&d.config, d)
if err != nil {
return err
Expand Down Expand Up @@ -118,12 +171,13 @@ func (d *Daemon) Run() error {
}

d.server = server.Server{
Config: d.config,
ContainerMgr: containerMgr,
SystemMgr: systemMgr,
ImageMgr: imageMgr,
VolumeMgr: volumeMgr,
NetworkMgr: networkMgr,
Config: d.config,
ContainerMgr: containerMgr,
SystemMgr: systemMgr,
ImageMgr: imageMgr,
VolumeMgr: volumeMgr,
NetworkMgr: networkMgr,
ContainerPlugin: d.containerPlugin,
}

// init base network
Expand Down Expand Up @@ -166,6 +220,7 @@ func (d *Daemon) Run() error {
logrus.Infof("GRPC server stopped")
<-streamServerCloseCh
logrus.Infof("Stream server stopped")

return nil
}

Expand Down Expand Up @@ -212,3 +267,19 @@ func (d *Daemon) MetaStore() *meta.Store {
func (d *Daemon) networkInit(ctx context.Context) error {
return mode.NetworkModeInit(ctx, d.config.NetworkConfg, d.networkMgr)
}

// ContainerPlugin returns the container plugin fetched from shared file
func (d *Daemon) ContainerPlugin() plugins.ContainerPlugin {
return d.containerPlugin
}

// ShutdownPlugin invoke pre-stop method in daemon plugin if exist
func (d *Daemon) ShutdownPlugin() error {
if d.daemonPlugin != nil {
logrus.Infof("invoke pre-stop hook in plugin")
if err := d.daemonPlugin.PreStopHook(); err != nil {
logrus.Errorf("stop prehook execute error %v", err)
}
}
return nil
}
47 changes: 31 additions & 16 deletions daemon/mgr/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"
"time"

"github.com/alibaba/pouch/apis/plugins"
"github.com/alibaba/pouch/apis/types"
"github.com/alibaba/pouch/ctrd"
"github.com/alibaba/pouch/daemon/config"
Expand Down Expand Up @@ -112,22 +113,25 @@ type ContainerManager struct {

// monitor is used to handle container's event, eg: exit, stop and so on.
monitor *ContainerMonitor

containerPlugin plugins.ContainerPlugin
}

// NewContainerManager creates a brand new container manager.
func NewContainerManager(ctx context.Context, store *meta.Store, cli *ctrd.Client, imgMgr ImageMgr, volMgr VolumeMgr, netMgr NetworkMgr, cfg *config.Config) (*ContainerManager, error) {
func NewContainerManager(ctx context.Context, store *meta.Store, cli *ctrd.Client, imgMgr ImageMgr, volMgr VolumeMgr, netMgr NetworkMgr, cfg *config.Config, contPlugin plugins.ContainerPlugin) (*ContainerManager, error) {
mgr := &ContainerManager{
Store: store,
NameToID: collect.NewSafeMap(),
Client: cli,
ImageMgr: imgMgr,
VolumeMgr: volMgr,
NetworkMgr: netMgr,
IOs: containerio.NewCache(),
ExecProcesses: collect.NewSafeMap(),
cache: collect.NewSafeMap(),
Config: cfg,
monitor: NewContainerMonitor(),
Store: store,
NameToID: collect.NewSafeMap(),
Client: cli,
ImageMgr: imgMgr,
VolumeMgr: volMgr,
NetworkMgr: netMgr,
IOs: containerio.NewCache(),
ExecProcesses: collect.NewSafeMap(),
cache: collect.NewSafeMap(),
Config: cfg,
monitor: NewContainerMonitor(),
containerPlugin: contPlugin,
}

mgr.Client.SetExitHooks(mgr.exitedAndRelease)
Expand Down Expand Up @@ -506,11 +510,22 @@ func (mgr *ContainerManager) Start(ctx context.Context, id, detachKeys string) (
s.Linux.CgroupsPath = filepath.Join(cgroupsParent, c.ID())
}

var prioArr []int
var argsArr [][]string
if mgr.containerPlugin != nil {
prioArr, argsArr, err = mgr.containerPlugin.PreStart(c)
if err != nil {
return errors.Wrapf(err, "get pre-start hook error from container plugin")
}
}

sw := &SpecWrapper{
s: s,
ctrMgr: mgr,
volMgr: mgr.VolumeMgr,
netMgr: mgr.NetworkMgr,
s: s,
ctrMgr: mgr,
volMgr: mgr.VolumeMgr,
netMgr: mgr.NetworkMgr,
prioArr: prioArr,
argsArr: argsArr,
}

for _, setup := range SetupFuncs() {
Expand Down
8 changes: 5 additions & 3 deletions daemon/mgr/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import (
type SpecWrapper struct {
s *specs.Spec

ctrMgr ContainerMgr
volMgr VolumeMgr
netMgr NetworkMgr
ctrMgr ContainerMgr
volMgr VolumeMgr
netMgr NetworkMgr
prioArr []int
argsArr [][]string
}

// SetupFunc defines spec setup function type.
Expand Down
Loading