Initial support for plugins
This commit is contained in:
parent
49b5ad83b9
commit
614d2b3015
@ -5,7 +5,9 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/auth/settings"
|
||||
"github.com/cortezaproject/corteza-server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"github.com/cortezaproject/corteza-server/pkg/plugin"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/spf13/cobra"
|
||||
@ -44,6 +46,9 @@ type (
|
||||
lvl int
|
||||
Log *zap.Logger
|
||||
|
||||
// Available plugins
|
||||
plugins plugin.Set
|
||||
|
||||
// Store interface
|
||||
//
|
||||
// Just a blank interface{} because we want to avoid generating
|
||||
@ -71,7 +76,7 @@ type (
|
||||
func New() *CortezaApp {
|
||||
app := &CortezaApp{
|
||||
lvl: bootLevelWaiting,
|
||||
Log: zap.NewNop(),
|
||||
Log: logger.Default(),
|
||||
}
|
||||
|
||||
app.InitCLI()
|
||||
|
||||
@ -54,8 +54,6 @@ const (
|
||||
|
||||
// Setup configures all required services
|
||||
func (app *CortezaApp) Setup() (err error) {
|
||||
app.Log = logger.Default()
|
||||
|
||||
if app.lvl >= bootLevelSetup {
|
||||
// Are basics already set-up?
|
||||
return nil
|
||||
@ -147,6 +145,10 @@ func (app *CortezaApp) Setup() (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
if err = app.plugins.Setup(app.Log.Named("plugin")); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
app.lvl = bootLevelSetup
|
||||
return
|
||||
}
|
||||
@ -414,6 +416,14 @@ func (app *CortezaApp) InitServices(ctx context.Context) (err error) {
|
||||
// Initializing seeder
|
||||
_ = seeder.Seeder(ctx, app.Store, seeder.Faker())
|
||||
|
||||
if err = app.plugins.Initialize(ctx, app.Log); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = app.plugins.RegisterAutomation(autService.Registry()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
app.lvl = bootLevelServicesInitialized
|
||||
return
|
||||
}
|
||||
|
||||
22
app/cli.go
22
app/cli.go
@ -8,9 +8,11 @@ import (
|
||||
federationCommands "github.com/cortezaproject/corteza-server/federation/commands"
|
||||
"github.com/cortezaproject/corteza-server/pkg/cli"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"github.com/cortezaproject/corteza-server/pkg/plugin"
|
||||
fakerCommands "github.com/cortezaproject/corteza-server/pkg/seeder/commands"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
systemCommands "github.com/cortezaproject/corteza-server/system/commands"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// InitCLI function initializes basic Corteza subsystems
|
||||
@ -24,7 +26,7 @@ func (app *CortezaApp) InitCLI() {
|
||||
envs []string
|
||||
)
|
||||
|
||||
app.Command = cli.RootCommand(func() error {
|
||||
app.Command = cli.RootCommand(func() (err error) {
|
||||
if len(envs) == 0 {
|
||||
envs = []string{"."}
|
||||
}
|
||||
@ -37,7 +39,23 @@ func (app *CortezaApp) InitCLI() {
|
||||
// loaded at this point!
|
||||
app.Opt = options.Init()
|
||||
|
||||
return nil
|
||||
app.Log.Warn("loading plugins", zap.String("paths", app.Opt.Plugins.Paths))
|
||||
if app.Opt.Plugins.Enabled {
|
||||
var paths []string
|
||||
paths, err = plugin.Resolve(app.Opt.Plugins.Paths)
|
||||
|
||||
app.Log.Warn("loading plugins", zap.Strings("paths", paths))
|
||||
|
||||
app.plugins, err = plugin.Load(paths...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Empty set of plugins
|
||||
app.plugins = plugin.Set{}
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
app.Command.Flags().StringSliceVar(&envs, "env-file", nil,
|
||||
|
||||
@ -26,6 +26,7 @@ type (
|
||||
RBAC RBACOpt
|
||||
Locale LocaleOpt
|
||||
Limit LimitOpt
|
||||
Plugins PluginsOpt
|
||||
}
|
||||
)
|
||||
|
||||
@ -55,5 +56,6 @@ func Init() *Options {
|
||||
RBAC: *RBAC(),
|
||||
Locale: *Locale(),
|
||||
Limit: *Limit(),
|
||||
Plugins: *Plugins(),
|
||||
}
|
||||
}
|
||||
|
||||
36
pkg/options/plugins.gen.go
generated
Normal file
36
pkg/options/plugins.gen.go
generated
Normal file
@ -0,0 +1,36 @@
|
||||
package options
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// pkg/options/plugins.yaml
|
||||
|
||||
type (
|
||||
PluginsOpt struct {
|
||||
Enabled bool `env:"PLUGINS_ENABLED"`
|
||||
Paths string `env:"PLUGINS_PATHS"`
|
||||
}
|
||||
)
|
||||
|
||||
// Plugins initializes and returns a PluginsOpt with default values
|
||||
func Plugins() (o *PluginsOpt) {
|
||||
o = &PluginsOpt{
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
fill(o)
|
||||
|
||||
// Function that allows access to custom logic inside the parent function.
|
||||
// The custom logic in the other file should be like:
|
||||
// func (o *Plugins) Defaults() {...}
|
||||
func(o interface{}) {
|
||||
if def, ok := o.(interface{ Defaults() }); ok {
|
||||
def.Defaults()
|
||||
}
|
||||
}(o)
|
||||
|
||||
return
|
||||
}
|
||||
12
pkg/options/plugins.yaml
Normal file
12
pkg/options/plugins.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
docs:
|
||||
title: Plugins
|
||||
description: Server plugins
|
||||
|
||||
props:
|
||||
- name: Enabled
|
||||
type: bool
|
||||
default: true
|
||||
description: Enable plugins
|
||||
|
||||
- name: Paths
|
||||
description: List of colon seperated paths or patterns where plugins could be found
|
||||
29
pkg/plugin/automation.go
Normal file
29
pkg/plugin/automation.go
Normal file
@ -0,0 +1,29 @@
|
||||
package plugin
|
||||
|
||||
// Collection of boot-lifecycle related functions
|
||||
// that exec plugin functions
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza-server/automation/types"
|
||||
sdk "github.com/cortezaproject/corteza-server/sdk/plugin"
|
||||
)
|
||||
|
||||
type (
|
||||
automationRegistry interface {
|
||||
AddFunctions(ff ...*types.Function)
|
||||
// AddTypes(tt ...expr.Type)
|
||||
}
|
||||
)
|
||||
|
||||
func (pp Set) RegisterAutomation(r automationRegistry) error {
|
||||
for _, p := range pp {
|
||||
d, is := p.def.(sdk.AutomationFunctionsProvider)
|
||||
if !is {
|
||||
continue
|
||||
}
|
||||
|
||||
r.AddFunctions(d.AutomationFunctions()...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
43
pkg/plugin/boot.go
Normal file
43
pkg/plugin/boot.go
Normal file
@ -0,0 +1,43 @@
|
||||
package plugin
|
||||
|
||||
// Collection of boot-lifecycle related functions
|
||||
// that exec plugin functions
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
sdk "github.com/cortezaproject/corteza-server/sdk/plugin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (pp Set) Setup(log *zap.Logger) error {
|
||||
for _, p := range pp {
|
||||
d, is := p.def.(sdk.Setup)
|
||||
if !is {
|
||||
continue
|
||||
}
|
||||
|
||||
err := d.Setup(log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pp Set) Initialize(ctx context.Context, log *zap.Logger) error {
|
||||
for _, p := range pp {
|
||||
d, is := p.def.(sdk.Initialize)
|
||||
if !is {
|
||||
continue
|
||||
}
|
||||
|
||||
err := d.Initialize(ctx, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
83
pkg/plugin/plugin.go
Normal file
83
pkg/plugin/plugin.go
Normal file
@ -0,0 +1,83 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"plugin"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
// Set of plugins
|
||||
Set []*item
|
||||
|
||||
// item represents a plugin
|
||||
item struct {
|
||||
src string
|
||||
def interface{}
|
||||
}
|
||||
)
|
||||
|
||||
// Resolve string with colon separated paths
|
||||
func Resolve(paths string) (out []string, err error) {
|
||||
var (
|
||||
matches []string
|
||||
)
|
||||
|
||||
for _, part := range strings.Split(paths, ":") {
|
||||
matches, err = filepath.Glob(part)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = append(out, matches...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Load loads plugins from all given paths
|
||||
func Load(paths ...string) (Set, error) {
|
||||
var set = Set{}
|
||||
|
||||
for _, path := range paths {
|
||||
|
||||
if info, err := os.Lstat(path); err != nil {
|
||||
return nil, err
|
||||
} else if info.IsDir() {
|
||||
return nil, fmt.Errorf("can not use directory %s as a plugin", path)
|
||||
}
|
||||
|
||||
if i, err := load(path); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
set = append(set, i)
|
||||
}
|
||||
}
|
||||
|
||||
return set, nil
|
||||
}
|
||||
|
||||
// load single plugin from the given path
|
||||
func load(path string) (i *item, err error) {
|
||||
i = &item{}
|
||||
p, err := plugin.Open(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
aux, err := p.Lookup("CortezaPlugin")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fn, is := aux.(func() interface{})
|
||||
if !is {
|
||||
return nil, fmt.Errorf("incompatible plugin definition")
|
||||
}
|
||||
|
||||
i.def = fn()
|
||||
return
|
||||
}
|
||||
26
sdk/plugin/plugin.go
Normal file
26
sdk/plugin/plugin.go
Normal file
@ -0,0 +1,26 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/automation/types"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type (
|
||||
Setup interface {
|
||||
Setup(log *zap.Logger) error
|
||||
}
|
||||
|
||||
Initialize interface {
|
||||
Initialize(ctx context.Context, log *zap.Logger) error
|
||||
}
|
||||
|
||||
AutomationFunctionsProvider interface {
|
||||
AutomationFunctions() []*types.Function
|
||||
}
|
||||
|
||||
//AutomationTypesProvider interface {
|
||||
// AutomationTypes() []*expr.Type
|
||||
//}
|
||||
)
|
||||
Loading…
x
Reference in New Issue
Block a user