github.com/lastbackend/toolkit@v0.0.0-20241020043710-cafa37b95aad/pkg/runtime/controller/plugin.go (about) 1 package controller 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "github.com/lastbackend/toolkit" 8 "github.com/lastbackend/toolkit/pkg/runtime" 9 "github.com/lastbackend/toolkit/pkg/runtime/logger" 10 "github.com/lastbackend/toolkit/pkg/util/types" 11 "golang.org/x/sync/errgroup" 12 "reflect" 13 ) 14 15 const PluginHookMethodPreStart = "PreStart" 16 const PluginHookMethodOnStart = "OnStart" 17 const PluginHookMethodOnStartSync = "OnStartSync" 18 const PluginHookMethodOnStop = "OnStop" 19 const PluginHookMethodOnStopSync = "OnStopSync" 20 21 type pluginManager struct { 22 runtime.Plugin 23 24 log logger.Logger 25 constructors []any 26 plugins []toolkit.Plugin 27 } 28 29 func (c *pluginManager) Provide(constructor ...any) { 30 c.constructors = append(c.constructors, constructor...) 31 } 32 33 func (c *pluginManager) Constructors() []any { 34 return c.constructors 35 } 36 37 func (c *pluginManager) Register(plugins []toolkit.Plugin) { 38 c.log.V(5).Info("pluginManager.Register.start") 39 c.plugins = append(c.plugins, plugins...) 40 c.log.V(5).Infof("pluginManager.Register.plugins %v", c.plugins) 41 c.log.V(5).Info("pluginManager.Register.end") 42 return 43 } 44 45 func (c *pluginManager) PreStart(ctx context.Context) error { 46 c.log.V(5).Info("pluginManager.PreStart.start") 47 err := c.hook(ctx, PluginHookMethodPreStart, true) 48 if err != nil { 49 return err 50 } 51 c.log.V(5).Info("pluginManager.PreStart.end") 52 return nil 53 } 54 55 func (c *pluginManager) OnStart(ctx context.Context) error { 56 c.log.V(5).Info("pluginManager.OnStart.start") 57 58 err := c.hook(ctx, PluginHookMethodOnStartSync, true) 59 if err != nil { 60 return err 61 } 62 63 err = c.hook(ctx, PluginHookMethodOnStart, false) 64 if err != nil { 65 return err 66 } 67 68 c.log.V(5).Info("pluginManager.OnStart.end") 69 return nil 70 } 71 72 func (c *pluginManager) OnStop(ctx context.Context) error { 73 c.log.V(5).Info("pluginManager.OnStop.start") 74 75 err := c.hook(ctx, PluginHookMethodOnStopSync, true) 76 if err != nil { 77 return err 78 } 79 80 err = c.hook(ctx, PluginHookMethodOnStop, false) 81 if err != nil { 82 return err 83 } 84 85 c.log.V(5).Info("pluginManager.OnStop.end") 86 return nil 87 } 88 89 func (c *pluginManager) hook(ctx context.Context, kind string, sync bool) error { 90 91 if sync { 92 c.log.V(5).Infof("pluginManager.%s.start:sync", kind) 93 defer func() { 94 c.log.V(5).Infof("pluginManager.%s.end:sync", kind) 95 }() 96 } else { 97 c.log.V(5).Infof("pluginManager.%s.start:async", kind) 98 defer func() { 99 c.log.V(5).Infof("pluginManager.%s.end:async", kind) 100 }() 101 } 102 103 // start all non-sync methods 104 g, ctx := errgroup.WithContext(ctx) 105 for i := 0; i < len(c.plugins); i++ { 106 107 if c.plugins[i] == nil { 108 continue 109 } 110 111 plugin := c.plugins[i] 112 113 if sync { 114 if err := c.call(ctx, plugin, kind); err != nil { 115 return err 116 } 117 } else { 118 g.Go(func() error { 119 return c.call(ctx, plugin, kind) 120 }) 121 } 122 } 123 124 if err := g.Wait(); err != nil { 125 c.log.V(5).Errorf("can not start toolkit:", err.Error()) 126 return err 127 } 128 129 return nil 130 131 } 132 133 func (c *pluginManager) call(ctx context.Context, pkg toolkit.Plugin, kind string) error { 134 135 args := []reflect.Value{reflect.ValueOf(ctx)} 136 meth := reflect.ValueOf(pkg).MethodByName(kind) 137 name := types.Type(pkg) 138 139 if !reflect.ValueOf(meth).IsZero() { 140 c.log.V(5).Infof("pluginManager.%s.call: %s", kind, name) 141 res := meth.Call(args) 142 143 if len(res) < 1 { 144 return nil 145 } 146 147 if len(res) > 1 { 148 return errors.New(fmt.Sprintf("pluginManager.%s.call:%s:err: method results are not supported. Only error is supported", kind, name)) 149 } 150 151 var err error 152 if v := res[0].Interface(); v != nil { 153 err = v.(error) 154 } 155 return err 156 } 157 158 return nil 159 } 160 161 func newPluginController(_ context.Context, runtime runtime.Runtime) runtime.Plugin { 162 pl := new(pluginManager) 163 pl.log = runtime.Log() 164 pl.constructors = make([]any, 0) 165 pl.plugins = make([]toolkit.Plugin, 0) 166 return pl 167 }